]> git.saurik.com Git - apple/libc.git/blobdiff - gen/_simple.c
Libc-498.tar.gz
[apple/libc.git] / gen / _simple.c
diff --git a/gen/_simple.c b/gen/_simple.c
new file mode 100644 (file)
index 0000000..9b8aeb1
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * Copyright (c) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#include <asl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "_simple.h"
+
+#define VM_PAGE_SIZE   4096
+#define SBUF_SIZE(s)   (((SBUF *)(s))->b.end - ((SBUF *)(s))->b.buf + 1)
+/* we use a small buffer to minimize stack usage constraints */
+#define MYBUFSIZE      32
+
+typedef struct _BUF {
+    char *buf;
+    char *ptr;
+    char *end;
+    int fd;
+    void (*full)(struct _BUF *);
+} BUF;
+
+typedef struct _SBUF {
+    BUF b;
+    jmp_buf j;
+} SBUF;
+
+static int asl_socket;
+static pthread_once_t asl_socket_once = PTHREAD_ONCE_INIT;
+
+/* private extern exports from asl.c */
+const char *_asl_escape(unsigned char);
+int _asl_server_socket(int *, struct sockaddr_un *);
+
+/* flush the buffer */
+static void
+_flush(BUF *b)
+{
+    char *buf = b->buf;
+    int n = b->ptr - buf;
+    int w;
+
+    while(n > 0) {
+       w = write(b->fd, buf, n);
+       if(w < 0) {
+           if(errno == EINTR || errno == EAGAIN)
+               continue;
+           break;
+       }
+       n -= w;
+       buf += n;
+    }
+}
+
+/* flush the buffer and reset the pointer */
+static void
+_flush_reset(BUF *b)
+{
+    _flush(b);
+    b->ptr = b->buf;
+}
+
+/* enlarge the buffer */
+static void
+_enlarge(BUF *b)
+{
+    vm_address_t new;
+    vm_size_t sold, snew;
+    intptr_t diff;
+
+    new = (vm_address_t)(b->end + 1);
+    if(vm_allocate(mach_task_self(), &new, VM_PAGE_SIZE, 0) == 0) {
+       /* page is adjacent */
+       b->end += VM_PAGE_SIZE;
+       return;
+    }
+    sold = SBUF_SIZE(b);
+    snew = (sold + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1);
+    if(vm_allocate(mach_task_self(), &new, snew, 1) != 0)
+       longjmp(((SBUF *)b)->j, 1); /* out of memory */
+    diff = new - (vm_address_t)b->buf;
+    memcpy((void *)new, b->buf, sold);
+    if((intptr_t)(b->buf) & (VM_PAGE_SIZE - 1)) {
+       sold &= ~(VM_PAGE_SIZE - 1);
+       b->buf = (char *)((intptr_t)(b->buf + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1));
+       b->end = (char *)(new + snew - 1);
+    } else
+       b->end += diff + VM_PAGE_SIZE;
+    if(sold > 0) {
+       vm_deallocate(mach_task_self(), (vm_address_t)b->buf, sold);
+    }
+    b->buf = (char *)new;
+    b->ptr += diff;
+}
+
+static inline void put_s(BUF *, _esc_func, const char *);
+/* output a single character */
+static inline void
+put_c(BUF *b, _esc_func esc, unsigned char c)
+{
+    const char *cp;
+
+    if(esc && (cp = esc(c)) != NULL)
+       put_s(b, NULL, cp);
+    else {
+       if(b->ptr >= b->end)
+           b->full(b);
+       *b->ptr++ = c;
+    }
+}
+
+/* output a null-terminated string */
+static inline void
+put_s(BUF *b, _esc_func esc, const char *str)
+{
+    while(*str)
+       put_c(b, esc, *str++);
+}
+
+/* output a string of the specified size */
+static inline void
+put_n(BUF *b, _esc_func esc, const char *str, int n)
+{
+    while(n-- > 0)
+       put_c(b, esc, *str++);
+}
+
+/*
+ * Output the signed decimal string representing the number in "in".  "width" is
+ * the minimum field width, and "zero" is a boolean value, true for zero padding
+ * (otherwise blank padding).
+ */
+static void
+dec(BUF *b, _esc_func esc, long long in, int width, int zero)
+{
+    char buf[32];
+    char *cp = buf + sizeof(buf);
+    int pad;
+    int neg = 0;
+    unsigned long long n = (unsigned long long)in;
+
+    if(in < 0) {
+       neg++;
+       width--;
+       n = ~n + 1;
+    }
+    *--cp = 0;
+    if(n) {
+       while(n) {
+           *--cp = (n % 10) + '0';
+           n /= 10;
+       }
+    } else
+       *--cp = '0';
+    if(neg && zero) {
+       put_c(b, esc, '-');
+       neg = 0;
+    }
+    pad = width - strlen(cp);
+    zero = zero ? '0' : ' ';
+    while(pad-- > 0)
+       put_c(b, esc, zero);
+    if(neg)
+       put_c(b, esc, '-');
+    put_s(b, esc, cp);
+}
+
+/*
+ * Output the hex string representing the number in "n".  "width" is the
+ * minimum field width, and "zero" is a boolean value, true for zero padding
+ * (otherwise blank padding).  "upper" is a boolean value, true for upper
+ * case hex characters, lower case otherwise.  "p" is a boolean value, true
+ * if 0x should be prepended (for %p), otherwise nothing.
+ */
+static char _h[] = "0123456789abcdef";
+static char _H[] = "0123456789ABCDEF";
+static char _0x[] = "0x";
+
+static void
+hex(BUF *b, _esc_func esc, unsigned long long n, int width, int zero, int upper, int p)
+{
+    char buf[32];
+    char *cp = buf + sizeof(buf);
+    char *h = upper ? _H : _h;
+
+    *--cp = 0;
+    if(n) {
+       while(n) {
+           *--cp = h[n & 0xf];
+           n >>= 4;
+       }
+    } else
+       *--cp = '0';
+    if(p) {
+       width -= 2;
+       if(zero) {
+           put_s(b, esc, _0x);
+           p = 0;
+       }
+    }
+    width -= strlen(cp);
+    zero = zero ? '0' : ' ';
+    while(width-- > 0)
+       put_c(b, esc, zero);
+    if(p)
+       put_s(b, esc, _0x);
+    put_s(b, esc, cp);
+}
+
+/*
+ * Output the unsigned decimal string representing the number in "n".  "width"
+ * is the minimum field width, and "zero" is a boolean value, true for zero
+ * padding (otherwise blank padding).
+ */
+static void
+udec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
+{
+    char buf[32];
+    char *cp = buf + sizeof(buf);
+    int pad;
+
+    *--cp = 0;
+    if(n) {
+       while(n) {
+           *--cp = (n % 10) + '0';
+           n /= 10;
+       }
+    } else
+       *--cp = '0';
+    pad = width - strlen(cp);
+    zero = zero ? '0' : ' ';
+    while(pad-- > 0)
+       put_c(b, esc, zero);
+    put_s(b, esc, cp);
+}
+
+/*
+ * Output the unsigned decimal string representing the number in "n", rounded
+ * to the nearest MB, KB or b.  "width" is the minimum field width, and "zero"
+ * is a boolean value, true for zero padding (otherwise blank padding).
+ */
+static void
+ydec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
+{
+    if(n >= 10 * (1 << 20)) {
+       n += (1 << 19);
+       udec(b, esc, n >> 20, width, zero);
+       put_s(b, esc, "MB");
+    } else if (n >= 10 * (1 << 10)) {
+       n += (1 << 9);
+       udec(b, esc, n >> 10, width, zero);
+       put_s(b, esc, "KB");
+    } else {
+       udec(b, esc, n, width, zero);
+       put_s(b, esc, "b");
+    }
+}
+
+/*
+ * The actual engine for all the _simple_*printf routines.
+ */
+static void
+__simple_bprintf(BUF *b, _esc_func esc, const char *fmt, va_list ap)
+{
+    while(*fmt) {
+       int lflag, zero, width;
+       char *cp;
+       if(!(cp = strchr(fmt, '%'))) {
+           put_s(b, esc, fmt);
+           break;
+       }
+       put_n(b, esc, fmt, cp - fmt);
+       fmt = cp + 1;
+       if(*fmt == '%') {
+           put_c(b, esc, '%');
+           fmt++;
+           continue;
+       }
+       lflag = zero = width = 0;
+       for(;;) {
+           switch(*fmt) {
+           case '0':
+               zero++;
+               fmt++;
+               /* drop through */
+           case '1': case '2': case '3': case '4': case '5':
+           case '6': case '7': case '8': case '9':
+               while(*fmt >= '0' && *fmt <= '9')
+                   width = 10 * width + (*fmt++ - '0');
+               continue;
+           case 'c':
+               zero = zero ? '0' : ' ';
+               width--;
+               while(width-- > 0)
+                   put_c(b, esc, zero);
+               put_c(b, esc, va_arg(ap, int));
+               break;
+           case 'd': case 'i':
+               switch(lflag) {
+               case 0:
+                   dec(b, esc, va_arg(ap, int), width, zero);
+                   break;
+               case 1:
+                   dec(b, esc, va_arg(ap, long), width, zero);
+                   break;
+               default:
+                   dec(b, esc, va_arg(ap, long long), width, zero);
+                   break;
+               }
+               break;
+           case 'l':
+               lflag++;
+               fmt++;
+               continue;
+           case 'p':
+               hex(b, esc, (unsigned long)va_arg(ap, void *), width, zero, 0, 1);
+               break;
+           case 's':
+               cp = va_arg(ap, char *);
+               width -= strlen(cp);
+               zero = zero ? '0' : ' ';
+               while(width-- > 0)
+                   put_c(b, esc, zero);
+               put_s(b, esc, cp);
+               break;
+           case 'u':
+               switch(lflag) {
+               case 0:
+                   udec(b, esc, va_arg(ap, unsigned int), width, zero);
+                   break;
+               case 1:
+                   udec(b, esc, va_arg(ap, unsigned long), width, zero);
+                   break;
+               default:
+                   udec(b, esc, va_arg(ap, unsigned long long), width, zero);
+                   break;
+               }
+               break;
+           case 'X': case 'x':
+               switch(lflag) {
+               case 0:
+                   hex(b, esc, va_arg(ap, unsigned int), width, zero,
+                       *fmt == 'X', 0);
+                   break;
+               case 1:
+                   hex(b, esc, va_arg(ap, unsigned long), width, zero,
+                       *fmt == 'X', 0);
+                   break;
+               default:
+                   hex(b, esc, va_arg(ap, unsigned long long), width, zero,
+                       *fmt == 'X', 0);
+                   break;
+               }
+               break;
+           case 'y':
+               switch(lflag) {
+               case 0:
+                   ydec(b, esc, va_arg(ap, unsigned int), width, zero);
+                   break;
+               case 1:
+                   ydec(b, esc, va_arg(ap, unsigned long), width, zero);
+                   break;
+               default:
+                   ydec(b, esc, va_arg(ap, unsigned long long), width, zero);
+                   break;
+               }
+               break;
+           default:
+               put_c(b, esc, *fmt);
+               break;
+           }
+           break;
+       }
+       fmt++;
+    }
+}
+
+/*
+ * A simplified vfprintf variant.  The format string is interpreted with
+ * arguments from the va_list, and the results are written to the given
+ * file descriptor.
+ */
+void
+_simple_vdprintf(int fd, const char *fmt, va_list ap)
+{
+    BUF b;
+    char buf[MYBUFSIZE];
+
+    b.buf = buf;
+    b.fd = fd;
+    b.ptr = b.buf;
+    b.end = b.buf + MYBUFSIZE;
+    b.full = _flush_reset;
+    __simple_bprintf(&b, NULL, fmt, ap);
+    _flush(&b);
+}
+
+/*
+ * A simplified fprintf variant.  The format string is interpreted with
+ * arguments from the variable argument list, and the results are written
+ * to the given file descriptor.
+ */
+void
+_simple_dprintf(int fd, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    _simple_vdprintf(fd, fmt, ap);
+    va_end(ap);
+}
+
+/*
+ * A simplified string allocate routine.  Pass the opaque pointer to structure
+ * to _simple_*sprintf() routines.  Use _simple_string() to retrieve the
+ * current string (the string is guaranteed to be null terminated only on
+ * the call to _simple_string()).  Use _simple_sfree() to free the structure
+ * and string memory.
+ */
+_SIMPLE_STRING
+_simple_salloc(void)
+{
+    SBUF *b;
+
+    if(vm_allocate(mach_task_self(), (vm_address_t *)&b, VM_PAGE_SIZE, 1))
+       return NULL;
+    b->b.ptr = b->b.buf = (char *)b + sizeof(SBUF);
+    b->b.end = (char *)b + VM_PAGE_SIZE - 1;
+    b->b.full = _enlarge;
+    return (_SIMPLE_STRING)b;
+}
+
+/*
+ * The format string is interpreted with arguments from the va_list, and the
+ * results are appended to the string maintained by the opaque structure, as
+ * returned by a previous call to _simple_salloc().  Non-zero is returned on
+ * out-of-memory error.
+ */
+int
+_simple_vsprintf(_SIMPLE_STRING b, const char *fmt, va_list ap)
+{
+    return _simple_vesprintf(b, NULL, fmt, ap);
+}
+
+/*
+ * The format string is interpreted with arguments from the variable argument
+ * list, and the results are appended to the string maintained by the opaque
+ * structure, as returned by a previous call to _simple_salloc().  Non-zero is
+ * returned on out-of-memory error.
+ */
+int
+_simple_sprintf(_SIMPLE_STRING b, const char *fmt, ...)
+{
+    va_list ap;
+    int ret;
+
+    va_start(ap, fmt);
+    ret = _simple_vesprintf(b, NULL, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+/*
+ * Like _simple_vsprintf(), except __esc is a function to call on each
+ * character; the function returns NULL if the character should be passed
+ * as is, otherwise, the returned character string is used instead.
+ */
+int
+_simple_vesprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, va_list ap)
+{
+    if(setjmp(((SBUF *)b)->j))
+       return -1;
+    __simple_bprintf((BUF *)b, esc, fmt, ap);
+    return 0;
+}
+
+/*
+ * Like _simple_sprintf(), except __esc is a function to call on each
+ * character; the function returns NULL if the character should be passed
+ * as is, otherwise, the returned character string is used instead.
+ */
+int _simple_esprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, ...)
+{
+    va_list ap;
+    int ret;
+
+    va_start(ap, fmt);
+    ret = _simple_vesprintf(b, esc, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+/*
+ * Return the null terminated string from the opaque structure, as returned
+ * by a previous call to _simple_salloc().
+ */
+char *
+_simple_string(_SIMPLE_STRING b)
+{
+    *((BUF *)b)->ptr = 0;
+    return ((BUF *)b)->buf;
+}
+
+/*
+ * Reposition the pointer to the first null in the buffer.  After a call to
+ * _simple_string, the buffer can be modified, and shrunk.
+ */
+void
+_simple_sresize(_SIMPLE_STRING b)
+{
+    ((BUF *)b)->ptr = ((BUF *)b)->buf + strlen(((BUF *)b)->buf);
+}
+
+/*
+ * Append the null-terminated string to the string associated with the opaque
+ * structure.  Non-zero is returned on out-of-memory error.
+ */
+int
+_simple_sappend(_SIMPLE_STRING b, const char *str)
+{
+    return _simple_esappend(b, NULL, str);
+}
+
+/*
+ * Like _simple_sappend(), except __esc is a function to call on each
+ * character; the function returns NULL if the character should be passed
+ * as is, otherwise, the returned character string is used instead.
+ */
+int _simple_esappend(_SIMPLE_STRING b, _esc_func esc, const char *str)
+{
+    if(setjmp(((SBUF *)b)->j))
+       return -1;
+    put_s((BUF *)b, esc, str);
+    return 0;
+}
+
+/*
+ * Write the string associated with the opaque structure to the file descriptor.
+ */
+void
+_simple_put(_SIMPLE_STRING b, int fd)
+{
+    ((BUF *)b)->fd = fd;
+    _flush((BUF *)b);
+}
+
+/*
+ * Write the string associated with the opaque structure and a trailing newline,
+ * to the file descriptor.
+ */
+void
+_simple_putline(_SIMPLE_STRING b, int fd)
+{
+    ((BUF *)b)->fd = fd;
+    *((BUF *)b)->ptr++ = '\n';
+    _flush((BUF *)b);
+    ((BUF *)b)->ptr--;
+}
+
+/*
+ * Free the opaque structure, and the associated string.
+ */
+void
+_simple_sfree(_SIMPLE_STRING b)
+{
+    vm_size_t s;
+
+    if(((intptr_t)(((SBUF *)b)->b.buf) & (VM_PAGE_SIZE - 1)) == 0) {
+       vm_deallocate(mach_task_self(), (vm_address_t)((SBUF *)b)->b.buf, SBUF_SIZE(b));
+       s = VM_PAGE_SIZE;
+    } else
+       s = ((SBUF *)b)->b.end - (char *)b + 1;
+    vm_deallocate(mach_task_self(), (vm_address_t)b, s);
+}
+
+/*
+ * Simplified ASL log interface; does not use malloc.  Unfortunately, this
+ * requires knowledge of the format used by ASL.
+ */
+static void
+socket_init(void)
+{
+    struct sockaddr_un server;
+    _asl_server_socket(&asl_socket, &server);
+}
+
+void
+_simple_asl_log(int level, const char *facility, const char *message)
+{
+    _SIMPLE_STRING b;
+
+    if(pthread_once(&asl_socket_once, socket_init) != 0)
+       return;
+    if((b = _simple_salloc()) == NULL)
+       return;
+    do {
+       char *cp, *bp;
+       unsigned u;
+       struct timeval tv;
+
+       if(_simple_sprintf(b, "%10u [Time ", 0))
+           break;
+       gettimeofday(&tv, NULL);
+       if(_simple_esprintf(b, _asl_escape, "%lu", tv.tv_sec))
+           break;
+       if(_simple_sappend(b, "] [Host] [Sender "))
+           break;
+       if(_simple_esappend(b, _asl_escape, getprogname()))
+           break;
+       if(_simple_sappend(b, "] [PID "))
+           break;
+       if(_simple_esprintf(b, _asl_escape, "%u", getpid()))
+           break;
+       if(_simple_sappend(b, "] [UID "))
+           break;
+       if(_simple_esprintf(b, _asl_escape, "%d", getuid()))
+           break;
+       if(_simple_sappend(b, "] [GID "))
+           break;
+       if(_simple_esprintf(b, _asl_escape, "%d", getgid()))
+           break;
+       if(_simple_sappend(b, "] [Level "))
+           break;
+       if(_simple_esprintf(b, _asl_escape, "%d", level))
+           break;
+       if(_simple_sappend(b, "] [Message "))
+           break;
+       if(_simple_esappend(b, _asl_escape, message))
+           break;
+       /* remove trailing (escaped) newlines */
+       cp = _simple_string(b);
+       cp += strlen(cp);
+       for(;;) {
+           cp -= 2;
+           if(strcmp(cp, "\\n") != 0)
+               break;
+           *cp = 0;
+       }
+       _simple_sresize(b);
+       if(_simple_sappend(b, "] [Facility "))
+           break;
+       if(_simple_esappend(b, _asl_escape, facility))
+           break;
+       if(_simple_sappend(b, "]\n"))
+           break;
+       cp = _simple_string(b);
+       u = strlen(cp) - 10; // includes newline and null
+       bp = cp + 10;
+       if(u == 0)
+           *--bp = '0';
+       else
+           while(bp > cp && u) {
+               *--bp = u % 10 + '0';
+               u /= 10;
+           }
+       write(asl_socket, cp, strlen(cp) + 1);
+    } while(0);
+    _simple_sfree(b);
+}