ref: fd731d3c833d9241fdff618b6c949453fb5c0f31
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Mon Mar 15 07:38:36 EDT 2021
dump it to git so I do not lose anything
--- /dev/null
+++ b/README.md
@@ -1,0 +1,14 @@
+# npe
+
+"Native Porting Environment" for 9front, pronounced "nope".
+
+*WIP*
+
+The project tries to provide a minimalistic (and mostly
+non-conformant) POSIX environment with several additional libraries
+(cut-down version of SDL2 from scratch for now) to ease up porting of
+software to 9front. The goal is to be able to build and run
+non-native GUI software with very minimal changes, if any.
+
+Unlike APE, NPE is fully native and is not trying to hide any of the
+native Plan 9 APIs.
--- /dev/null
+++ b/SDL2/SDL.h
@@ -1,0 +1,431 @@
+#ifndef _SDL_h_
+#define _SDL_h_
+
+#include "plan9.h"
+#include <keyboard.h>
+
+typedef int SDL_AudioDeviceID;
+typedef struct SDL_AudioSpec SDL_AudioSpec;
+typedef struct SDL_Window SDL_Window;
+typedef struct SDL_Renderer SDL_Renderer;
+typedef struct SDL_Texture SDL_Texture;
+typedef struct SDL_Surface SDL_Surface;
+typedef struct SDL_Rect SDL_Rect;
+typedef int SDL_Keycode;
+typedef int SDL_Scancode;
+typedef int SDL_Keymod;
+typedef struct SDL_Event SDL_Event;
+typedef u8int Uint8;
+typedef u16int Uint16;
+typedef u32int Uint32;
+typedef int SDL_BlendMode;
+typedef struct SDL_Thread SDL_Thread;
+typedef int (*SDL_ThreadFunction)(void *);
+typedef struct SDL_Cursor SDL_Cursor;
+typedef struct SDL_PixelFormat SDL_PixelFormat;
+typedef struct SDL_Point SDL_Point;
+typedef int SDL_RendererFlip;
+typedef struct SDL_DisplayMode SDL_DisplayMode;
+
+#pragma incomplete SDL_Cursor
+#pragma incomplete SDL_PixelFormat
+#pragma incomplete SDL_Renderer
+#pragma incomplete SDL_Texture
+#pragma incomplete SDL_Thread
+#pragma incomplete SDL_Window
+
+typedef enum {SDL_FALSE, SDL_TRUE} SDL_bool;
+#define SDLCALL
+
+void SDL_StopTextInput(void);
+SDL_bool SDL_HasSSE(void);
+SDL_bool SDL_HasSSE2(void);
+int SDL_Init(int);
+int SDL_EventState(Uint32, int);
+SDL_Keymod SDL_GetModState(void);
+char *SDL_GetAudioDeviceName(int, SDL_bool);
+int SDL_GetNumAudioDevices(int iscapture);
+int SDL_ShowCursor(int toggle);
+void SDL_LockAudioDevice(SDL_AudioDeviceID);
+void SDL_UnlockAudioDevice(SDL_AudioDeviceID);
+void SDL_PauseAudioDevice(SDL_AudioDeviceID, SDL_bool);
+void SDL_CloseAudioDevice(SDL_AudioDeviceID);
+SDL_AudioDeviceID SDL_OpenAudioDevice(char *, int, SDL_AudioSpec *, SDL_AudioSpec *, u32int);
+u64int SDL_GetPerformanceFrequency(void);
+u64int SDL_GetPerformanceCounter(void);
+char *SDL_GetError(void);
+SDL_Thread *SDL_CreateThread(SDL_ThreadFunction, char *, void *);
+void SDL_DetachThread(SDL_Thread *);
+char *SDL_GetClipboardText(void);
+int SDL_SetClipboardText(char *);
+void SDL_SetThreadPriority(int);
+SDL_bool SDL_HasClipboardText(void);
+int SDL_PollEvent(SDL_Event *event);
+void SDL_RestoreWindow(SDL_Window *window);
+void SDL_RaiseWindow(SDL_Window *window);
+int SDL_UpdateTexture(SDL_Texture *texture, SDL_Rect *rect, void *pixels, int pitch);
+int SDL_RenderClear(SDL_Renderer *renderer);
+int SDL_GetWindowDisplayIndex(SDL_Window *window);
+void SDL_FreeSurface(SDL_Surface *surface);
+Uint32 SDL_GetGlobalMouseState(int *x, int *y);
+void SDL_Quit(void);
+void SDL_free(void *);
+SDL_Cursor *SDL_GetDefaultCursor(void);
+void SDL_SetCursor(SDL_Cursor *cursor);
+void SDL_FreeCursor(SDL_Cursor *cursor);
+SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
+SDL_Surface *SDL_CreateRGBSurfaceFrom(Uint32 *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am);
+Uint32 SDL_MapRGB(SDL_PixelFormat *format, Uint8 r, Uint8 g, Uint8 b);
+int SDL_SetColorKey(SDL_Surface *surface, int flag, Uint32 key);
+int SDL_SetSurfaceRLE(SDL_Surface *surface, int flag);
+int SDL_LockSurface(SDL_Surface *surface);
+int SDL_UnlockSurface(SDL_Surface *surface);
+int SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode);
+SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y);
+int SDL_PushEvent(SDL_Event *event);
+void SDL_WarpMouseInWindow(SDL_Window *window, int x, int y);
+void SDL_RenderGetScale(SDL_Renderer *renderer, float *scaleX, float *scaleY);
+void SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
+void SDL_GetWindowPosition(SDL_Window *window, int *x, int *y);
+Uint32 SDL_GetMouseState(int *x, int *y);
+SDL_bool SDL_IsTextInputActive(void);
+void SDL_StartTextInput(void);
+void SDL_Delay(Uint32 ms);
+int SDL_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcrect, SDL_Rect *dstrect);
+void SDL_RenderPresent(SDL_Renderer *renderer);
+Uint32 SDL_GetWindowFlags(SDL_Window *window);
+int SDL_RenderSetLogicalSize(SDL_Renderer *renderer, int w, int h);
+void SDL_SetWindowSize(SDL_Window *window, int w, int h);
+int SDL_ShowSimpleMessageBox(Uint32 flags, char *title, char *message, SDL_Window *window);
+int SDL_SetWindowFullscreen(SDL_Window *window, Uint32 flags);
+void SDL_SetWindowGrab(SDL_Window *window, SDL_bool grabbed);
+void SDL_SetWindowPosition(SDL_Window *window, int x, int y);
+void SDL_DestroyTexture(SDL_Texture *texture);
+void SDL_DestroyRenderer(SDL_Renderer *renderer);
+void SDL_DestroyWindow(SDL_Window *window);
+int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode);
+void SDL_SetWindowTitle(SDL_Window *window, char *title);
+int SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode);
+SDL_bool SDL_SetHint(char *name, char *value);
+SDL_Window *SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32 flags);
+SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, int index, Uint32 flags);
+int SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode);
+char *SDL_GetCurrentVideoDriver(void);
+SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h);
+void SDL_EnableScreenSaver(void);
+Uint32 SDL_GetTicks(void);
+int SDL_GetRendererOutputSize(SDL_Renderer *renderer, int *w, int *h);
+int SDL_SaveBMP(SDL_Surface *s, const char *file);
+
+enum {
+ AUDIO_S16,
+ AUDIO_F32,
+
+ SDL_AUDIO_ALLOW_ANY_CHANGE = ~0,
+
+ SDL_THREAD_PRIORITY_HIGH = 1,
+
+ SDL_QUERY = -1,
+ SDL_DISABLE,
+ SDL_ENABLE,
+
+ SDL_MESSAGEBOX_ERROR = 0,
+
+ SDL_WINDOW_MINIMIZED = 1<<0,
+ SDL_WINDOW_FULLSCREEN_DESKTOP = 1<<1,
+ SDL_WINDOW_INPUT_FOCUS = 1<<2,
+ SDL_WINDOW_ALLOW_HIGHDPI = 1<<3,
+ SDL_WINDOW_SHOWN = 1<<4,
+
+ SDL_WINDOWPOS_CENTERED = -1,
+ SDL_WINDOWPOS_UNDEFINED = -2,
+
+ SDL_INIT_AUDIO = 1<<0,
+ SDL_INIT_VIDEO = 1<<1,
+ SDL_INIT_JOYSTICK = 1<<2,
+
+ SDL_BLENDMODE_NONE = 0,
+
+ SDL_FLIP_NONE = 0,
+ SDL_FLIP_HORIZONTAL,
+ SDL_FLIP_VERTICAL,
+
+ SDL_PIXELFORMAT_ARGB8888 = 0x30128888,
+
+ /* shit no one cares about */
+ SDL_TEXTUREACCESS_STREAMING = 0,
+ SDL_TEXTUREACCESS_STATIC = 0,
+ SDL_RENDERER_ACCELERATED = 0,
+ SDL_RENDERER_PRESENTVSYNC = 0,
+};
+
+enum {
+ /* these HAVE to be in this order for notes to work in ft2-clone */
+ SDL_SCANCODE_A = 0x04,
+ SDL_SCANCODE_B,
+ SDL_SCANCODE_C,
+ SDL_SCANCODE_D,
+ SDL_SCANCODE_E,
+ SDL_SCANCODE_F,
+ SDL_SCANCODE_G,
+ SDL_SCANCODE_H,
+ SDL_SCANCODE_I,
+ SDL_SCANCODE_J,
+ SDL_SCANCODE_K,
+ SDL_SCANCODE_L,
+ SDL_SCANCODE_M,
+ SDL_SCANCODE_N,
+ SDL_SCANCODE_O,
+ SDL_SCANCODE_P,
+ SDL_SCANCODE_Q,
+ SDL_SCANCODE_R,
+ SDL_SCANCODE_S,
+ SDL_SCANCODE_T,
+ SDL_SCANCODE_U,
+ SDL_SCANCODE_V,
+ SDL_SCANCODE_W,
+ SDL_SCANCODE_X,
+ SDL_SCANCODE_Y,
+ SDL_SCANCODE_Z,
+ SDL_SCANCODE_1,
+ SDL_SCANCODE_2,
+ SDL_SCANCODE_3,
+ SDL_SCANCODE_4,
+ SDL_SCANCODE_5,
+ SDL_SCANCODE_6,
+ SDL_SCANCODE_7,
+ SDL_SCANCODE_8,
+ SDL_SCANCODE_9,
+ SDL_SCANCODE_0,
+ SDL_SCANCODE_ENTER,
+ SDL_SCANCODE_ESCAPE,
+ SDL_SCANCODE_BACKSPACE,
+ SDL_SCANCODE_TAB,
+ SDL_SCANCODE_SPACE,
+ SDL_SCANCODE_MINUS,
+ SDL_SCANCODE_EQUALS,
+ SDL_SCANCODE_LEFTBRACKET,
+ SDL_SCANCODE_RIGHTBRACKET,
+ SDL_SCANCODE_BACKSLASH,
+ SDL_SCANCODE_NONUSHASH,
+ SDL_SCANCODE_SEMICOLON,
+ SDL_SCANCODE_APOSTROPHE,
+ SDL_SCANCODE_GRAVE,
+ SDL_SCANCODE_SLASH,
+ SDL_SCANCODE_CAPSLOCK,
+
+ SDL_SCANCODE_RIGHT = 0x4f,
+ SDL_SCANCODE_LEFT,
+ SDL_SCANCODE_DOWN,
+ SDL_SCANCODE_UP,
+ SDL_SCANCODE_NUMLOCKCLEAR,
+ SDL_SCANCODE_KP_DIVIDE,
+ SDL_SCANCODE_KP_MULTIPLY,
+ SDL_SCANCODE_KP_MINUS,
+ SDL_SCANCODE_KP_PLUS,
+ SDL_SCANCODE_KP_ENTER,
+ SDL_SCANCODE_KP_1,
+ SDL_SCANCODE_KP_2,
+ SDL_SCANCODE_KP_3,
+ SDL_SCANCODE_KP_4,
+ SDL_SCANCODE_KP_5,
+ SDL_SCANCODE_KP_6,
+ SDL_SCANCODE_KP_7,
+ SDL_SCANCODE_KP_8,
+ SDL_SCANCODE_KP_9,
+ SDL_SCANCODE_KP_0,
+ SDL_SCANCODE_KP_PERIOD,
+ SDL_SCANCODE_NONUSBACKSLASH,
+
+ SDL_SCANCODE_MUTE = 0x7f,
+ SDL_SCANCODE_VOLUMEUP,
+ SDL_SCANCODE_VOLUMEDOWN,
+
+ SDL_SCANCODE_AUDIOMUTE = 0x106,
+
+ SDLK_a = 'a',
+ SDLK_b,
+ SDLK_c,
+ SDLK_d,
+ SDLK_e,
+ SDLK_f,
+ SDLK_g,
+ SDLK_h,
+ SDLK_i,
+ SDLK_j,
+ SDLK_k,
+ SDLK_l,
+ SDLK_m,
+ SDLK_n,
+ SDLK_o,
+ SDLK_p,
+ SDLK_q,
+ SDLK_r,
+ SDLK_s,
+ SDLK_t,
+ SDLK_u,
+ SDLK_v,
+ SDLK_w,
+ SDLK_x,
+ SDLK_y,
+ SDLK_z,
+ SDLK_0 = '0',
+ SDLK_1,
+ SDLK_2,
+ SDLK_3,
+ SDLK_4,
+ SDLK_5,
+ SDLK_6,
+ SDLK_7,
+ SDLK_8,
+ SDLK_9,
+ SDLK_DELETE = Kdel,
+ SDLK_RETURN = '\n',
+ SDLK_ESCAPE = Kesc,
+ SDLK_LESS = '<',
+ SDLK_SPACE = ' ',
+ SDLK_TAB = '\t',
+ SDLK_LEFT = Kleft,
+ SDLK_RIGHT = Kright,
+ SDLK_DOWN = Kdown,
+ SDLK_UP = Kup,
+ SDLK_F1 = KF|1,
+ SDLK_F2,
+ SDLK_F3,
+ SDLK_F4,
+ SDLK_F5,
+ SDLK_F6,
+ SDLK_F7,
+ SDLK_F8,
+ SDLK_F9,
+ SDLK_F10,
+ SDLK_F11,
+ SDLK_F12,
+ SDLK_INSERT = Kins,
+ SDLK_PAGEUP = Kpgup,
+ SDLK_PAGEDOWN = Kpgdown,
+ SDLK_HOME = Khome,
+ SDLK_END = Kend,
+ SDLK_BACKSPACE = Kbs,
+ SDLK_MINUS = '-',
+ SDLK_PLUS = '+',
+ SDLK_EQUALS = '=',
+ SDLK_UNDERSCORE = '_',
+
+ SDLK_LALT = Kalt,
+ SDLK_RALT = Kaltgr, /* FIXME what about keyboards without it? */
+ /* FIXME no distinction */
+ SDLK_LSHIFT = Kshift,
+ SDLK_RSHIFT = SDLK_LSHIFT,
+ SDLK_LCTRL = Kctl,
+ SDLK_RCTRL = '>', /* FIXME this is a hack */
+
+ /* FIXME not bound to anything */
+ SDLK_UNKNOWN = -99999,
+ SDLK_CAPSLOCK,
+ SDLK_KP_ENTER,
+
+ /* FIXME no distinction */
+ KMOD_LSHIFT = 1<<0,
+ KMOD_RSHIFT = KMOD_LSHIFT,
+ KMOD_LCTRL = 1<<1,
+ KMOD_RCTRL = KMOD_LCTRL,
+ KMOD_LALT = 1<<2,
+ KMOD_RALT = KMOD_LALT,
+ KMOD_LGUI = 1<<3,
+ KMOD_RGUI = KMOD_LGUI,
+
+ SDL_KEYDOWN = 0,
+ SDL_KEYUP,
+ SDL_MOUSEBUTTONDOWN,
+ SDL_MOUSEBUTTONUP,
+ SDL_MOUSEWHEEL,
+ SDL_MOUSEMOTION,
+ SDL_QUIT,
+ SDL_DROPFILE,
+ SDL_TEXTINPUT,
+ SDL_WINDOWEVENT,
+ SDL_WINDOWEVENT_HIDDEN,
+ SDL_WINDOWEVENT_SHOWN,
+ SDL_WINDOWEVENT_MINIMIZED,
+ SDL_WINDOWEVENT_FOCUS_LOST,
+ SDL_WINDOWEVENT_EXPOSED,
+
+ SDL_BUTTON_LEFT = 0,
+ SDL_BUTTON_MIDDLE = 1,
+ SDL_BUTTON_RIGHT = 2,
+
+ SDL_BUTTON_LMASK = 1<<SDL_BUTTON_LEFT,
+ SDL_BUTTON_MMASK = 1<<SDL_BUTTON_MIDDLE,
+ SDL_BUTTON_RMASK = 1<<SDL_BUTTON_RIGHT,
+};
+
+struct SDL_AudioSpec {
+ void (*callback)(void *, Uint8 *, int);
+ void *userdata;
+ int freq;
+ int format;
+ int channels;
+ int samples;
+};
+
+struct SDL_Point {
+ int x, y;
+};
+
+struct SDL_Rect {
+ int x, y, w, h;
+};
+
+struct SDL_Surface {
+ SDL_PixelFormat *format;
+ SDL_Rect clip_rect;
+ Uint32 flags;
+ Uint32 key;
+ int keyset;
+ int w, h;
+ int pitch;
+ uchar pixels[];
+};
+
+struct SDL_Event {
+ int type;
+ union {
+ struct {
+ int event;
+ }window;
+ struct {
+ struct {
+ SDL_Scancode scancode;
+ SDL_Keycode sym;
+ }keysym;
+ int repeat;
+ }key;
+ struct {
+ int x, y;
+ int button;
+ }button;
+ struct {
+ int x, y;
+ }motion;
+ struct {
+ char text[UTFmax+1];
+ }text;
+ struct {
+ int x, y;
+ }wheel;
+ struct {
+ char *file;
+ }drop;
+ };
+};
+
+struct SDL_DisplayMode
+{
+ int format;
+ int w;
+ int h;
+ int refresh_rate;
+};
+
+#endif
--- /dev/null
+++ b/alloca.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/assert.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/ctype.h
@@ -1,0 +1,7 @@
+#ifndef _ctype_h_
+#define _ctype_h_
+
+#include "plan9.h"
+#include </sys/include/ctype.h>
+
+#endif
--- /dev/null
+++ b/dirent.c
@@ -1,0 +1,59 @@
+#include <dirent.h>
+
+struct DIR {
+ Dir *d;
+ struct dirent de;
+ int fd;
+ long n; /* number of entries in d */
+ long i; /* current entry to return from readdir */
+};
+
+struct dirent *
+readdir(DIR *d)
+{
+ if(d->i >= d->n){
+ free(d->d);
+ if((d->n = dirread(d->fd, &d->d)) <= 0)
+ return nil;
+ d->i = 0;
+ }
+
+ d->de.d_type = (d->d[d->i].qid.type & QTDIR) ? DT_DIR : DT_FILE;
+ d->de.d_name = d->d[d->i].name;
+ d->i++;
+
+ return &d->de;
+}
+
+DIR *
+opendir(char *name)
+{
+ Dir *dir;
+ DIR *d;
+ int fd;
+
+ dir = nil;
+ d = nil;
+ if((fd = open(name, OREAD|OCEXEC)) >= 0 &&
+ (dir = dirfstat(fd)) != nil &&
+ (dir->qid.type & QTDIR) != 0 &&
+ (d = calloc(1, sizeof(*d))) != nil){
+ d->fd = fd;
+ }
+
+ free(dir);
+ if(d == nil)
+ close(fd);
+
+ return d;
+}
+
+int
+closedir(DIR *d)
+{
+ free(d->d);
+ close(d->fd);
+ free(d);
+
+ return 0;
+}
--- /dev/null
+++ b/dirent.h
@@ -1,0 +1,25 @@
+#ifndef _dirent_h_
+#define _dirent_h_
+
+#include "plan9.h"
+
+enum {
+ DT_LNK = -1,
+ DT_UNKNOWN = 0,
+ DT_FILE,
+ DT_DIR,
+};
+
+struct dirent {
+ int d_type;
+ char *d_name;
+};
+
+typedef struct DIR DIR;
+#pragma incomplete DIR
+
+DIR *opendir(char *name);
+int closedir(DIR *dirp);
+struct dirent *readdir(DIR *dirp);
+
+#endif
--- /dev/null
+++ b/iconv.c
@@ -1,0 +1,125 @@
+#include <iconv.h>
+
+enum {
+ Itranslit = 1<<0, /* no transliteration is performed :( */
+ Iignore = 1<<1, /* this option is ignored (pun TOTALLY intended) if Itranslit is set */
+
+ Tcs = 0,
+ Us,
+};
+
+struct iconv_s {
+ char to[16];
+ char from[16];
+ int fd;
+};
+
+static int
+cp2tcs(char *cp, char *tcs, int sz)
+{
+ int opt, n, i;
+ char *s;
+
+ opt = 0;
+ n = 0;
+ for(s = cp; (s = strstr(s, "//")) != nil;){
+ if(n == 0)
+ n = (int)(s - cp);
+ s += 2;
+ if(strncmp(s, "TRANSLIT", 8) == 0 && (s[8] == 0 || s[8] == '/'))
+ opt |= Itranslit;
+ else if(strncmp(s, "IGNORE", 6) == 0 && (s[6] == 0 || s[6] == '/'))
+ opt |= Iignore;
+ }
+ if(n < 1)
+ n = strlen(cp);
+
+ if(cp[0] >= '0' && cp[0] <= '9'){ /* ibm */
+ n = snprint(tcs, sz, "ibm%.*s", n, cp);
+ }else if(strncmp(cp, "ISO", 3) == 0){
+ if(cp[3] == '-' || cp[3] == '_'){
+ n--;
+ cp++;
+ }
+ n = snprint(tcs, sz, "%.*s", n-3, cp+3);
+ }else if(strncmp(cp, "CP12", 4) == 0){
+ n = snprint(tcs, sz, "windows-12%.*s", n-4, cp+4);
+ }else{ /* last chance, convert to lowercase. FIXME not all are supported, no checks are made */
+ n = snprint(tcs, sz, "%.*s", n, cp);
+ for(i = 0; i < n; i++)
+ tcs[i] = tolower(tcs[i]);
+ }
+
+ return n >= sz ? -1 : opt;
+}
+
+iconv_t
+iconv_open(char *to, char *from)
+{
+ int opt, pid, p[2];
+ iconv_t ic;
+
+ p[0] = p[1] = -1;
+ if((ic = calloc(1, sizeof(*ic))) == nil)
+ goto err;
+ if(cp2tcs(from, ic->from, sizeof(ic->from)) < 0)
+ goto err;
+ if((opt = cp2tcs(to, ic->to, sizeof(ic->to))) < 0)
+ goto err;
+ if(pipe(p) < 0)
+ goto err;
+ if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){
+ dup(p[Tcs], 0);
+ dup(p[Tcs], 1);
+ close(p[0]);
+ close(p[1]);
+ close(2); /* comment it out if you need debugging */
+ execl("/bin/tcs", "tcs", "-f", ic->from, "-t", ic->to, (opt == Iignore) ? "-c" : nil, nil);
+ sysfatal("execl: %r");
+ }else if(pid < 0){
+ goto err;
+ }
+ close(p[Tcs]);
+ ic->fd = p[Us];
+
+ setmalloctag(ic, getcallerpc(&to));
+
+ return ic;
+err:
+ close(p[0]);
+ close(p[1]);
+ if(ic != nil)
+ free(ic);
+ return (iconv_t)-1;
+}
+
+void
+iconv_close(iconv_t ic)
+{
+ close(ic->fd);
+ free(ic);
+}
+
+size_t
+iconv(iconv_t ic, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+ int n;
+
+ if(inbuf == nil || *inbuf == nil){ /* flush */
+ write(ic->fd, "", 0);
+ }else{
+ if((n = write(ic->fd, *inbuf, *inbytesleft)) < 0)
+ return -1;
+ *inbuf += n;
+ *inbytesleft -= n;
+ return 0;
+ }
+ if(outbuf == nil || *outbuf == nil) /* nothing to do */
+ return 0;
+ if((n = read(ic->fd, *outbuf, *outbytesleft)) < 0)
+ return -1;
+ *outbuf += n;
+ *outbytesleft -= n;
+
+ return n;
+}
--- /dev/null
+++ b/iconv.h
@@ -1,0 +1,14 @@
+#ifndef _iconv_h_
+#define _iconv_h_
+
+#include "plan9.h"
+#include <stdint.h>
+
+typedef struct iconv_s *iconv_t;
+#pragma incomplete struct iconv_s
+
+iconv_t iconv_open(char *to, char *from);
+void iconv_close(iconv_t);
+size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+
+#endif
--- /dev/null
+++ b/limits.h
@@ -1,0 +1,18 @@
+#ifndef _limits_h_
+#define _limits_h_
+
+#define INT16_MAX 0x7fff
+#define INT16_MIN ((int16_t)0x8000)
+#define INT64_MIN ((int64_t)0x8000000000000000ULL)
+#define INT32_MAX 0x7fffffff
+#define INT_MAX INT32_MAX
+#define INT32_MIN (-INT32_MAX-1)
+#define INT_MIN INT32_MIN
+#define SHRT_MAX 0x7fff
+#define SHRT_MIN (-SHRT_MAX-1)
+#define SIZE_MAX 0xffffffffU
+#define UINT32_MAX 0xffffffffU
+#define UINT_MAX UINT32_MAX
+#define UINT64_MAX 0xffffffffffffffffULL
+
+#endif
--- /dev/null
+++ b/malloc.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/math.c
@@ -1,0 +1,71 @@
+#include <math.h>
+
+/* some of these were taken from musl */
+
+#define ln2o1 1.4426950408889634073599
+#define dbleps 2.2204460492503131e-16
+#define huge 1.79769313486231e+308
+
+static double toint = 1.0/dbleps;
+
+double
+round(double x)
+{
+ union {double f; u64int i;} u = {x};
+ int e = u.i >> 52 & 0x7ff;
+ double y;
+
+ if(e >= 0x3ff+52)
+ return x;
+ if(u.i >> 63)
+ x = -x;
+ if(e < 0x3ff-1)
+ return 0*u.f;
+ y = x + toint - toint - x;
+ if(y > 0.5)
+ y = y + x - 1;
+ else if(y <= -0.5)
+ y = y + x + 1;
+ else
+ y = y + x;
+ if(u.i >> 63)
+ y = -y;
+
+ return y;
+}
+
+double
+log2(double x)
+{
+ if(x == 0)
+ return -huge;
+ if(x < 0 || isNaN(x))
+ return NaN();
+
+ return log(x)*ln2o1;
+}
+
+double
+trunc(double x)
+{
+ union {double f; u64int i;} u = {x};
+ int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12;
+ u64int m;
+
+ if(e >= 52 + 12)
+ return x;
+ if(e < 12)
+ e = 1;
+ m = -1ULL >> e;
+ if((u.i & m) == 0)
+ return x;
+ u.i &= ~m;
+
+ return u.f;
+}
+
+double
+exp2(double x)
+{
+ return exp(x/ln2o1);
+}
--- /dev/null
+++ b/math.h
@@ -1,0 +1,18 @@
+#ifndef _math_h_
+#define _math_h_
+
+#include "plan9.h"
+
+#define sinf sin
+#define cosf cos
+#define fabsf fabs
+#define fmodf fmod
+#define ceilf ceil
+#define floorf floor
+
+double round(double);
+double log2(double);
+double trunc(double);
+double exp2(double);
+
+#endif
--- /dev/null
+++ b/plan9.c
@@ -1,0 +1,86 @@
+#include "plan9.h"
+#include <thread.h>
+#include <tos.h>
+#include <pool.h>
+
+int mainstacksize = 32*1024;
+
+int
+rm_r(char *dir)
+{
+ int pid;
+
+ if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){
+ execl("/bin/rm", "rm", "-r", dir, nil);
+ sysfatal("execl: %r");
+ }
+
+ return pid < 0 ? -1 : 0;
+}
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uvlong
+nanosec(void)
+{
+ static uvlong fasthz, xstart;
+ uvlong x, div;
+
+ if(fasthz == ~0ULL)
+ return nsec() - xstart;
+
+ if(fasthz == 0){
+ if(_tos->cyclefreq){
+ cycles(&xstart);
+ fasthz = _tos->cyclefreq;
+ } else {
+ xstart = nsec();
+ fasthz = ~0ULL;
+ fprint(2, "cyclefreq not available, falling back to nsec()\n");
+ fprint(2, "you might want to disable aux/timesync\n");
+ return 0;
+ }
+ }
+ cycles(&x);
+ x -= xstart;
+
+ /* this is ugly */
+ for(div = NSEC; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+ return x / (fasthz / div);
+}
+
+void
+nsleep(uvlong ns)
+{
+ uvlong start, end;
+
+ start = nanosec();
+ end = start + ns;
+ ns = start;
+ do{
+ if(end - ns > 750000000ULL)
+ sleep(70);
+ else if (end - ns > 25000000ULL)
+ sleep(20);
+ else if (end - ns > 10000000ULL)
+ sleep(1);
+ else
+ break;
+ ns = nanosec();
+ }while(ns < end);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ int __main(int argc, char *argv[]);
+
+ setfcr(getfcr() & ~(FPINVAL|FPOVFL));
+ //mainmem->flags |= POOL_PARANOIA | POOL_ANTAGONISM;
+
+ threadexitsall(__main(argc, argv) == 0 ? nil : "error");
+}
--- /dev/null
+++ b/plan9.h
@@ -1,0 +1,18 @@
+#ifndef __plan9_h__
+#define __plan9_h__
+
+#include <u.h>
+#include <libc.h>
+#include </sys/include/stdio.h>
+
+#define PATH_MAX 256
+
+#define __attribute__(a)
+#define main __main
+#define NSEC 1000000000ULL
+
+int rm_r(char *dir);
+uvlong nanosec(void);
+void nsleep(uvlong ns);
+
+#endif
--- /dev/null
+++ b/sdl2.c
@@ -1,0 +1,1335 @@
+#include "plan9.h"
+#include <stdint.h>
+#include <tos.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <thread.h>
+#include <SDL2/SDL.h>
+#include <pool.h>
+
+typedef struct Audiodev Audiodev;
+
+enum {
+ Aout = 2,
+ Arec,
+
+ /* FIXME missing plumber→dropfile and enter()→text */
+ Ckey = 0,
+ Ckeytype,
+ Cmouse,
+ Cresize,
+ Numchan,
+
+ Rdown = 0,
+ Rup,
+ Rrepeat,
+
+ Audiobufsz = 16384,
+};
+
+struct SDL_Window {
+ int dummy;
+};
+
+struct SDL_Renderer {
+ int dummy;
+};
+
+struct SDL_Texture {
+ Memimage;
+};
+
+struct SDL_PixelFormat {
+ int format;
+};
+
+struct SDL_Cursor {
+ Image *i;
+ Image *m;
+ Point hot;
+};
+
+struct SDL_Thread {
+ SDL_ThreadFunction f;
+ char *name;
+ void *userdata;
+};
+
+static SDL_PixelFormat argb8888 = {
+ .format = SDL_PIXELFORMAT_ARGB8888,
+};
+
+struct Audiodev {
+ QLock;
+ int paused;
+ int fd;
+ int pid;
+ int mode;
+ Uint8 buf[Audiobufsz];
+ void *userdata;
+ void (*cb)(void *, Uint8 *, int);
+};
+
+/* FIXME extra USB audio devices? */
+static Audiodev au[4] = {
+ [0] = {.fd = -1, .mode = -1},
+ [1] = {.fd = -1, .mode = -1},
+ [Aout] = {.fd = -1, .pid = -1, .mode = OWRITE},
+ [Arec] = {.fd = -1, .pid = -1, .mode = OREAD},
+};
+
+static SDL_Window onewin;
+static SDL_Renderer oneren;
+static int kmod;
+static Mouse mouse, oldmouse;
+static Rune rune;
+static Mousectl *mctl;
+static Keyboardctl kctl;
+static Memimage *back;
+static u8int *backcopy;
+static Image *front;
+static int logiw, logih;
+static int physw, physh;
+static int forceredraw = 1;
+static SDL_Cursor *oldcursor, *cursor;
+static int mouseredraw = 0;
+static int showcursor = SDL_ENABLE;
+static int textinput;
+
+static Cursor nocursor = {
+ {0, 0},
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+};
+
+static Alt salt[Numchan+1] = {
+ [Ckey] = { nil, &rune, CHANRCV },
+ [Ckeytype] = { nil, nil, CHANNOP },
+ [Cmouse] = { nil, &mouse, CHANRCV },
+ [Cresize] = { nil, nil, CHANRCV },
+ [Numchan] = { nil, nil, CHANNOBLK },
+};
+
+static void
+kbdproc(void *)
+{
+ char buf[128], buf2[128], *s;
+ int kfd, n, kbin, t;
+ Rune r, o;
+
+ threadsetname("kbdproc");
+ if((kfd = open("/dev/kbd", OREAD|OCEXEC)) < 0)
+ sysfatal("/dev/kbd: %r");
+ kbin = open("/dev/kbin", OWRITE|OCEXEC);
+
+ buf2[0] = 0;
+ buf2[1] = 0;
+ buf[0] = 0;
+ kmod = 0;
+ o = 0;
+ for(;;){
+ if(buf[0] != 0){
+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){
+ n = read(kfd, buf, sizeof(buf)-1);
+ if(n <= 0)
+ break;
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+
+ switch(buf[0]){
+ case 'c':
+ if(chartorune(&r, buf+1) > 0 && r != Runeerror){
+ t = Rrepeat;
+ if(!textinput)
+ r = tolowerrune(r);
+ if(textinput || o != r){
+ send(salt[Ckey].c, &r);
+ send(salt[Ckeytype].c, &t);
+ }
+ o = 0;
+ }
+ default:
+ continue;
+
+ case 'k':
+ s = buf+1;
+ while(*s){
+ s += chartorune(&r, s);
+ if(utfrune(buf2+1, r) == nil){
+ if(r == Kalt){
+ /* magic trick: write Alt scancode to disable the "compose" mode */
+ /* FIXME: does this work in both native AND drawterm? */
+ write(kbin, "\x46", 1);
+ kmod |= KMOD_LALT;
+ }else if (r == Kshift)
+ kmod |= KMOD_LSHIFT;
+ else if(r == Kctl)
+ kmod |= KMOD_LCTRL;
+ else{
+ t = Rdown;
+ send(salt[Ckey].c, &r);
+ send(salt[Ckeytype].c, &t);
+ o = r;
+ }
+ }
+ }
+ break;
+
+ case 'K':
+ s = buf2+1;
+ while(*s){
+ s += chartorune(&r, s);
+ if(utfrune(buf+1, r) == nil){
+ if(r == Kalt)
+ kmod &= ~KMOD_LALT;
+ else if(r == Kshift)
+ kmod &= ~KMOD_LSHIFT;
+ else if(r == Kctl)
+ kmod &= ~KMOD_LCTRL;
+ else{
+ t = Rup;
+ send(salt[Ckey].c, &r);
+ send(salt[Ckeytype].c, &t);
+ }
+ }
+ }
+ break;
+ }
+ strcpy(buf2, buf);
+ }
+
+ threadexits(nil);
+}
+
+int
+SDL_Init(int mask)
+{
+ /* FIXME actually use the mask? */
+ USED(mask);
+
+ if(memimageinit() < 0)
+ goto err;
+ if(initdraw(nil, nil, "ft2-clone") < 0)
+ goto err;
+ if((mctl = initmouse(nil, screen)) == nil)
+ goto err;
+
+ salt[Ckey].c = chancreate(sizeof(Rune), 20);
+ salt[Ckeytype].c = chancreate(sizeof(int), 20);
+ salt[Cmouse].c = mctl->c;
+ salt[Cresize].c = mctl->resizec;
+ kctl.c = salt[Ckey].c; /* for enter() */
+
+ if(salt[Ckey].c == nil || salt[Ckeytype].c == nil || proccreate(kbdproc, nil, 4096) < 0)
+ goto err;
+
+ return 0;
+err:
+ werrstr("SDL_Init: %r");
+ return -1;
+}
+
+int
+SDL_EventState(Uint32, int)
+{
+ return 0;
+}
+
+SDL_Keymod
+SDL_GetModState(void)
+{
+ return kmod;
+}
+
+int
+SDL_ShowCursor(int toggle)
+{
+ if(toggle == SDL_QUERY)
+ return showcursor;
+
+ showcursor = toggle == SDL_ENABLE;
+ setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
+
+ return showcursor;
+}
+
+int
+SDL_GetNumAudioDevices(int iscapture)
+{
+ /* FIXME look for extra USB devices? */
+ USED(iscapture);
+ return 1;
+}
+
+char *
+SDL_GetAudioDeviceName(int index, int iscapture)
+{
+ /* FIXME look for extra USB devices? */
+ USED(index); USED(iscapture);
+ return "/dev/audio";
+}
+
+void
+SDL_LockAudioDevice(SDL_AudioDeviceID id)
+{
+ qlock(&au[id]);
+}
+
+void
+SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
+{
+ qunlock(&au[id]);
+}
+
+static void
+audiothread(void *p)
+{
+ Audiodev *d;
+
+ d = p;
+ threadsetname("audio %s", d == &au[Aout] ? "out" : "in");
+
+ for(;;){
+ qlock(d);
+ if(d->paused)
+ memset(d->buf, 0, sizeof(d->buf));
+ else
+ d->cb(d->userdata, d->buf, sizeof(d->buf));
+ qunlock(d);
+
+ if(write(d->fd, d->buf, sizeof(d->buf)) != sizeof(d->buf))
+ break;
+ }
+
+ threadexits(nil);
+}
+
+void
+SDL_PauseAudioDevice(SDL_AudioDeviceID id, SDL_bool pause)
+{
+ Audiodev *a;
+
+ a = &au[id];
+ if(a->paused && !pause){
+ if(a->pid < 0)
+ a->pid = proccreate(audiothread, a, mainstacksize);
+ a->paused = 0;
+ }else if(!a->paused && pause){
+ a->paused = 1;
+ }
+}
+
+SDL_AudioDeviceID
+SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
+{
+ Audiodev *a;
+ SDL_AudioDeviceID id;
+
+ /* FIXME look for extra USB devices? */
+ USED(dev);
+
+ if(change != SDL_AUDIO_ALLOW_ANY_CHANGE){ /* FIXME sampling in mono */
+ werrstr("SDL_OpenAudioDevice: changes not implemented");
+ return 0;
+ }
+
+ have->freq = 44100;
+ have->format = AUDIO_S16;
+ have->channels = 2;
+ have->samples = want->samples;
+
+ id = rec ? Arec : Aout;
+ a = &au[id];
+
+ if(a->fd < 0 && (a->fd = open("/dev/audio", a->mode|OCEXEC)) < 0){
+ werrstr("SDL_OpenAudioDevice: %r");
+ return 0;
+ }
+
+ a->userdata = want->userdata;
+ a->cb = want->callback;
+ a->paused = 1;
+
+ return id;
+}
+
+void
+SDL_CloseAudioDevice(SDL_AudioDeviceID id)
+{
+ Audiodev *a;
+
+ a = &au[id];
+ qlock(a);
+ close(a->fd);
+ a->fd = -1;
+ a->pid = -1;
+ a->userdata = nil;
+ a->cb = nil;
+ qunlock(a);
+}
+
+u64int
+SDL_GetPerformanceFrequency(void)
+{
+ return _tos->cyclefreq;
+}
+
+u64int
+SDL_GetPerformanceCounter(void)
+{
+ u64int x;
+
+ cycles(&x);
+
+ return x;
+}
+
+char *
+SDL_GetError(void)
+{
+ static char err[256];
+
+ snprint(err, sizeof(err), "%r");
+
+ return err;
+}
+
+static void
+sdlthread(void *p)
+{
+ SDL_Thread t;
+
+ t = *(SDL_Thread*)p;
+ if(t.name != nil)
+ threadsetname(t.name);
+ free(p);
+
+ threadexits(t.f(t.userdata) == 0 ? nil : "error");
+}
+
+SDL_Thread *
+SDL_CreateThread(SDL_ThreadFunction f, char *name, void *userdata)
+{
+ SDL_Thread *t;
+
+ if((t = calloc(1, sizeof(*t))) == nil)
+ return nil;
+
+ t->f = f;
+ t->name = name;
+ t->userdata = userdata;
+
+ if(proccreate(sdlthread, t, mainstacksize) < 0){
+ free(t);
+ t = nil;
+ }
+
+ return t;
+}
+
+void
+SDL_DetachThread(SDL_Thread *)
+{
+}
+
+static void *
+readfile(char *path, int *got)
+{
+ void *data, *data2;
+ int f, n, r, sz;
+
+ if((f = open(path, OREAD|OCEXEC)) < 0)
+ return nil;
+
+ sz = 32768;
+ data = nil;
+ for(n = 0;; n += r){
+ if(sz-n < 65536){
+ sz *= 2;
+ if((data2 = realloc(data, sz)) == nil)
+ goto err;
+ data = data2;
+ }
+ if((r = read(f, (char*)data+n, sz-n-1)) < 0)
+ goto err;
+ if(r == 0)
+ break;
+ }
+
+ if(got != nil)
+ *got = n;
+ ((char*)data)[n] = 0;
+
+ return data;
+err:
+ free(data);
+ close(f);
+ return nil;
+}
+
+char *
+SDL_GetClipboardText(void)
+{
+ return readfile("/dev/snarf", nil);
+}
+
+int
+SDL_SetClipboardText(char *s)
+{
+ int f, n;
+
+ n = -1;
+ if((f = open("/dev/snarf", OWRITE|OTRUNC|OCEXEC)) >= 0){
+ n = strlen(s);
+ n = write(f, s, n) == n ? 0 : -1;
+ close(f);
+ }
+
+ if(n != 0)
+ werrstr("SDL_SetClipboardText: %r");
+
+ return n;
+}
+
+SDL_Texture *
+SDL_CreateTexture(SDL_Renderer *, Uint32 format, int, int w, int h)
+{
+ SDL_Texture *t;
+
+ if(format != SDL_PIXELFORMAT_ARGB8888){
+ werrstr("SDL_CreateTexture: only SDL_PIXELFORMAT_ARGB8888 is supported");
+ goto err;
+ }
+ if((t = (SDL_Texture*)allocmemimage(Rect(0, 0, w, h), XRGB32)) == nil) /* FIXME ARGB32? */
+ goto err;
+ memfillcolor(t, DTransparent);
+
+ return t;
+err:
+ werrstr("SDL_CreateTexture: %r");
+ return nil;
+}
+
+int
+SDL_UpdateTexture(SDL_Texture *t, SDL_Rect *re, void *pixels, int pitch)
+{
+ Rectangle r;
+
+ r = re ? Rect(re->x, re->y, re->x+re->w, re->y+re->h) : t->r;
+ /* FIXME non-ARGB8888 */
+ /* FIXME pitch */ USED(pitch);
+ if(loadmemimage(t, r, pixels, Dx(r)*Dy(r)*4) < 0){
+ werrstr("SDL_UpdateTexture: %r");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+SDL_RenderClear(SDL_Renderer *)
+{
+ if(back != nil)
+ memfillcolor(back, DBlack);
+
+ return 0;
+}
+
+int
+SDL_GetWindowDisplayIndex(SDL_Window *)
+{
+ return 0;
+}
+
+void
+SDL_Quit(void)
+{
+ threadexitsall(nil);
+}
+
+void
+SDL_free(void *p)
+{
+ free(p);
+}
+
+SDL_Surface *
+SDL_CreateRGBSurface(Uint32, int w, int h, int bpp, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{
+ SDL_Surface *s;
+ int n;
+
+ USED(rm, gm, bm, am); /* FIXME masks */
+
+ n = w*h*bpp/8;
+ if((s = calloc(1, sizeof(*s)+n)) == nil){
+ werrstr("SDL_CreateRGBSurface: memory");
+ return nil;
+ }
+ s->format = &argb8888;
+ s->w = w;
+ s->h = h;
+ s->pitch = w*bpp/8;
+ s->clip_rect.x = 0;
+ s->clip_rect.y = 0;
+ s->clip_rect.w = w;
+ s->clip_rect.h = h;
+
+ return s;
+}
+
+SDL_Surface *
+SDL_CreateRGBSurfaceFrom(Uint32 *pixels, int w, int h, int bpp, int pitch, Uint32 rm, Uint32 gm, Uint32 bm, Uint32 am)
+{
+ SDL_Surface *s;
+ int n;
+
+ USED(pitch); /* FIXME pitch */
+
+ if((s = SDL_CreateRGBSurface(0, w, h, bpp, rm, gm, bm, am)) == nil)
+ return nil;
+
+ n = w*h*bpp/8;
+ memmove(s->pixels, pixels, n);
+
+ return s;
+}
+
+void
+SDL_FreeSurface(SDL_Surface *surface)
+{
+ memset(surface, 0, sizeof(surface));
+ free(surface);
+}
+
+Uint32
+SDL_MapRGB(SDL_PixelFormat *, Uint8 r, Uint8 g, Uint8 b)
+{
+ return r<<24 | g<<16 | b<<8 | 0xff;
+}
+
+int
+SDL_SetColorKey(SDL_Surface *s, int flag, Uint32 key)
+{
+ s->keyset = flag;
+ s->key = key;
+ return 0;
+}
+
+SDL_Cursor *
+SDL_CreateColorCursor(SDL_Surface *s, int hot_x, int hot_y)
+{
+ SDL_Cursor *c;
+ Rectangle r;
+ uchar *m;
+ int n;
+
+ m = nil;
+ if((c = calloc(1, sizeof(*c))) == nil){
+ werrstr("memory");
+ goto err;
+ }
+
+ r = Rect(0, 0, s->w, s->h);
+ if(s->keyset){
+ if((c->m = allocimage(display, r, GREY8, 0, DTransparent)) == nil)
+ goto err;
+ if((m = malloc(s->w * s->h)) == nil)
+ goto err;
+ for(n = 0; n < s->w * s->h; n++){
+ m[n] = ((u32int*)s->pixels)[n] == s->key ? 0x00 : 0xff;
+ if(m[n] == 0)
+ ((u32int*)s->pixels)[n] = 0;
+ }
+ if(loadimage(c->m, r, m, n) < 1)
+ goto err;
+ free(m);
+ m = nil;
+ }
+ if((c->i = allocimage(display, r, s->keyset ? XRGB32 : ARGB32, 0, DTransparent)) == nil)
+ goto err;
+ n = s->w * s->h * 4; /* FIXME non-ARGB8888 */
+ if(loadimage(c->i, r, s->pixels, n) < 1)
+ goto err;
+
+ c->hot = Pt(hot_x, hot_y);
+
+ return c;
+err:
+ werrstr("SDL_CreateColorCursor: %r");
+ if(c != nil){
+ freeimage(c->i);
+ freeimage(c->m);
+ }
+ free(c);
+ free(m);
+ return nil;
+}
+
+SDL_Cursor *
+SDL_GetDefaultCursor(void)
+{
+ return nil;
+}
+
+void
+SDL_SetCursor(SDL_Cursor *c)
+{
+ if(cursor != c){
+ cursor = c;
+ mouseredraw = 1;
+ setcursor(mctl, (cursor == nil && showcursor) ? nil : &nocursor);
+ }
+}
+
+void
+SDL_FreeCursor(SDL_Cursor *c)
+{
+ freeimage(c->i);
+ free(c);
+ if(cursor == c){
+ oldcursor = nil;
+ cursor = nil;
+ }
+}
+
+static int
+rune2scancode(Rune r)
+{
+ if(r >= 'a' && r <= 'z')
+ return r - 'a' + SDL_SCANCODE_A;
+ if(r >= '1' && r <= '9')
+ return r - '1' + SDL_SCANCODE_1;
+ if(r == '0') return SDL_SCANCODE_0;
+ if(r == '\n') return SDL_SCANCODE_ENTER;
+ if(r == Kesc) return SDL_SCANCODE_ESCAPE;
+ if(r == Kbs) return SDL_SCANCODE_BACKSPACE;
+ if(r == '\t') return SDL_SCANCODE_TAB;
+ if(r == ' ') return SDL_SCANCODE_SPACE;
+ if(r == '-') return SDL_SCANCODE_MINUS;
+ if(r == '=') return SDL_SCANCODE_EQUALS;
+ if(r == '[') return SDL_SCANCODE_LEFTBRACKET;
+ if(r == ']') return SDL_SCANCODE_RIGHTBRACKET;
+ if(r == '\\') return SDL_SCANCODE_BACKSLASH;
+ if(r == ';') return SDL_SCANCODE_SEMICOLON;
+ if(r == '\'') return SDL_SCANCODE_APOSTROPHE;
+ if(r == '/') return SDL_SCANCODE_SLASH;
+
+ if(r == Kright) return SDL_SCANCODE_RIGHT;
+ if(r == Kleft) return SDL_SCANCODE_LEFT;
+ if(r == Kdown) return SDL_SCANCODE_DOWN;
+ if(r == Kup) return SDL_SCANCODE_UP;
+
+/* FIXME
+ SDL_SCANCODE_NUMLOCKCLEAR,
+ SDL_SCANCODE_KP_DIVIDE,
+ SDL_SCANCODE_KP_MULTIPLY,
+ SDL_SCANCODE_KP_MINUS,
+ SDL_SCANCODE_KP_PLUS,
+ SDL_SCANCODE_KP_ENTER,
+ SDL_SCANCODE_KP_1,
+ SDL_SCANCODE_KP_2,
+ SDL_SCANCODE_KP_3,
+ SDL_SCANCODE_KP_4,
+ SDL_SCANCODE_KP_5,
+ SDL_SCANCODE_KP_6,
+ SDL_SCANCODE_KP_7,
+ SDL_SCANCODE_KP_8,
+ SDL_SCANCODE_KP_9,
+ SDL_SCANCODE_KP_0,
+ SDL_SCANCODE_KP_PERIOD,
+ SDL_SCANCODE_NONUSBACKSLASH,
+ SDL_SCANCODE_NONUSHASH,
+*/
+ /* FIXME there are some missing */
+
+ if(r == L'`' || r == L'´') /* FIXME this is most likely wrong */
+ return SDL_SCANCODE_GRAVE;
+
+ return r;
+}
+
+int
+SDL_PollEvent(SDL_Event *e)
+{
+ int t, down;
+
+ if(e == nil) /* FIXME need to buffer the event so it won't get lost */
+ return 0;
+
+ switch(alt(salt)){
+ case Ckey:
+ recv(salt[Ckeytype].c, &t);
+ if(textinput && rune >= 0x20 && rune <= 0x7f){
+ if(t != Rrepeat)
+ break;
+ e->type = SDL_TEXTINPUT;
+ e->text.text[runetochar(e->text.text, &rune)] = 0;
+ }else if(textinput && t != Rrepeat){
+ break;
+ }else{
+ e->type = (t == Rup) ? SDL_KEYUP : SDL_KEYDOWN;
+ e->key.repeat = !textinput && t == Rrepeat;
+ e->key.keysym.scancode = rune2scancode(rune);
+ e->key.keysym.sym = rune;
+ }
+ return 1;
+
+ case Cmouse:
+ e->motion.x = mouse.xy.x - screen->r.min.x;
+ e->motion.y = mouse.xy.y - screen->r.min.y;
+ if(!eqpt(mouse.xy, oldmouse.xy)){
+ mouseredraw = 1;
+ if(mouse.buttons == oldmouse.buttons){
+ e->type = SDL_MOUSEMOTION;
+ return 1;
+ }
+ }
+ if(mouse.buttons == oldmouse.buttons)
+ break;
+ /* FIXME there is a lot of hope for both buttons to never change at the same time */
+ if((down = (mouse.buttons & 1)) != (oldmouse.buttons & 1)){ /* left */
+ e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+ e->button.button = SDL_BUTTON_LEFT;
+ oldmouse.buttons = (oldmouse.buttons & ~1) | (mouse.buttons & 1);
+ return 1;
+ }
+ if((down = (mouse.buttons & 4)) != (oldmouse.buttons & 4)){ /* right */
+ e->type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
+ e->button.button = SDL_BUTTON_RIGHT;
+ oldmouse.buttons = (oldmouse.buttons & ~4) | (mouse.buttons & 4);
+ return 1;
+ }
+ if(mouse.buttons & (8|16)){
+ e->type = SDL_MOUSEWHEEL;
+ e->wheel.x = 0;
+ e->wheel.y = (mouse.buttons & 8) ? -1 : 1;
+ return 1;
+ }
+ break;
+
+ case Cresize:
+ forceredraw = 1;
+ if(getwindow(display, Refnone) < 0){
+ fprint(2, "%r\n");
+ /* FIXME do something here? */
+ //threadexitsall(nil);
+ }
+ e->type = SDL_WINDOWEVENT;
+ e->window.event = SDL_WINDOWEVENT_EXPOSED;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+SDL_PushEvent(SDL_Event *event)
+{
+ /* FIXME does it matter? */
+ USED(event);
+ return -1;
+}
+
+void
+SDL_WarpMouseInWindow(SDL_Window *, int x, int y)
+{
+ moveto(mctl, Pt(screen->r.min.x+x, screen->r.min.y+y));
+}
+
+Uint32
+SDL_GetGlobalMouseState(int *x, int *y)
+{
+ Uint32 b;
+
+ if(x != nil)
+ *x = mouse.xy.x;
+ if(y != nil)
+ *y = mouse.xy.y;
+
+ b = 0;
+ if(mouse.buttons & 1)
+ b |= SDL_BUTTON_LMASK;
+ if(mouse.buttons & 4)
+ b |= SDL_BUTTON_RMASK;
+ /* FIXME no middle button use AT ALL? */
+
+ return b;
+}
+
+Uint32
+SDL_GetMouseState(int *x, int *y)
+{
+ Uint32 b;
+
+ b = SDL_GetGlobalMouseState(nil, nil);
+ if(x != nil)
+ *x = (mouse.xy.x - screen->r.min.x) * logiw / physw;
+ if(y != nil)
+ *y = (mouse.xy.y - screen->r.min.y) * logih / physh;
+
+ return b;
+}
+
+void
+SDL_RenderGetScale(SDL_Renderer *, float *scaleX, float *scaleY)
+{
+ *scaleX = 1.0;
+ *scaleY = 1.0;
+}
+
+void
+SDL_GetWindowSize(SDL_Window *, int *w, int *h)
+{
+ /* no matter what rio decides */
+ *w = physw;
+ *h = physh;
+}
+
+void
+SDL_GetWindowPosition(SDL_Window *, int *x, int *y)
+{
+ *x = screen->r.min.x;
+ *y = screen->r.min.y;
+}
+
+SDL_bool
+SDL_IsTextInputActive(void)
+{
+ return textinput;
+}
+
+void
+SDL_StartTextInput(void)
+{
+ textinput = SDL_TRUE;
+}
+
+void
+SDL_StopTextInput(void)
+{
+ textinput = SDL_FALSE;
+}
+
+void
+SDL_Delay(Uint32 ms)
+{
+ nsleep((uvlong)ms*1000000ULL);
+}
+
+static void *
+resize(u32int *src, int iw, int ih, u32int *dst, int ow, int oh)
+{
+ int i, j, m, n;
+ u32int *d;
+
+ if(iw == ow && ih == oh)
+ return src;
+
+ d = dst;
+ n = ow/iw;
+ for(; ih > 0 && oh > 0; ih--){
+ for(i = j = 0; i < iw; i++, src++)
+ for(m = 0; m < n && j < ow; m++, j++)
+ *dst++ = *src;
+ oh--;
+ for(m = 1; m < n && oh > 0; m++){
+ memmove(dst, dst-j, j*4);
+ dst += j;
+ oh--;
+ }
+ }
+
+ return d;
+}
+
+int
+SDL_RenderCopy(SDL_Renderer *, SDL_Texture *t, SDL_Rect *sre, SDL_Rect *dre)
+{
+ Rectangle sr, dr;
+
+ if(sre != nil){
+ sr.min = Pt(sre->x, sre->y);
+ sr.max = addpt(sr.min, Pt(sre->w, sre->h));
+ }else
+ sr = t->r;
+
+ if(dre != nil){
+ dr.min = Pt(dre->x, dre->y);
+ dr.max = addpt(dr.min, Pt(dre->w, dre->h));
+ }else /* stretch */
+ dr = Rect(0, 0, logiw, logih);
+
+ if(back == nil || Dx(back->r) != logiw || Dy(back->r) != logih){
+ freememimage(back);
+ back = allocmemimage(Rect(0, 0, logiw, logih), XRGB32);
+ if(back == nil){
+ werrstr("SDL_RenderCopy: %r");
+ return -1;
+ }
+ free(backcopy);
+ backcopy = malloc(logiw*logih*4);
+ }
+
+ if(dre == nil)
+ resize((u32int*)byteaddr(t, ZP), Dx(sr), Dy(sr), (u32int*)byteaddr(back, ZP), logiw, logih);
+ else
+ memimagedraw(back, dr, t, sr.min, nil, ZP, S);
+
+ return 0;
+}
+
+void
+SDL_RenderPresent(SDL_Renderer *)
+{
+ Rectangle r, clipr;
+ static u32int *b;
+ uchar *rb;
+
+ if(!forceredraw && (forceredraw = memcmp(backcopy, byteaddr(back, ZP), logiw*logih*4)) == 0 && !mouseredraw)
+ return;
+
+ r = Rect(0, 0, physw, physh);
+ if(front != nil && (Dx(front->r) != physw || Dy(front->r) != physh)){
+ freeimage(front);
+ front = nil;
+ free(b);
+ b = nil;
+ }
+ if(b == nil && (b = realloc(b, physw*physh*4)) == nil){
+ fprint(2, "SDL_RenderPresent: %r\n");
+ return;
+ }
+ if(forceredraw || front == nil){
+ rb = resize((u32int*)byteaddr(back, ZP), Dx(back->r), Dy(back->r), b, physw, physh);
+ if(front == nil && (front = allocimage(display, r, XRGB32, 0, DNofill)) == nil){
+ fprint(2, "SDL_RenderPresent: %r\n");
+ return;
+ }
+ if(loadimage(front, r, rb, Dx(r)*Dy(r)*4) < 0){
+ fprint(2, "SDL_RenderPresent: %r\n");
+ return;
+ }
+ }
+
+ if(cursor != nil && showcursor){
+ r.min = subpt(mouse.xy, cursor->hot);
+ r.max = addpt(r.min, cursor->i->r.max);
+ if(!forceredraw && oldcursor != nil){
+ clipr.min = subpt(oldmouse.xy, oldcursor->hot);
+ clipr.max = addpt(clipr.min, oldcursor->i->r.max);
+ combinerect(&clipr, r);
+ replclipr(screen, 0, clipr);
+ }
+ }
+ draw(screen, screen->r, front, nil, ZP);
+ if(cursor != nil && showcursor)
+ draw(screen, r, cursor->i, cursor->m, ZP);
+ mouseredraw = 0;
+ oldmouse.xy = mouse.xy;
+ oldcursor = cursor;
+
+ flushimage(display, 1);
+
+ if(forceredraw)
+ memmove(backcopy, byteaddr(back, ZP), logiw*logih*4);
+ else
+ replclipr(screen, 0, screen->r);
+ forceredraw = 0;
+}
+
+Uint32
+SDL_GetWindowFlags(SDL_Window *)
+{
+ /* FIXME is this correct? */
+ return SDL_WINDOW_INPUT_FOCUS;
+}
+
+int
+SDL_RenderSetLogicalSize(SDL_Renderer *, int w, int h)
+{
+ if(logiw != w || logih != h){
+ logiw = w;
+ logih = h;
+ forceredraw = 1;
+ }
+
+ return 0;
+}
+
+int
+SDL_GetRendererOutputSize(SDL_Renderer *, int *w, int *h)
+{
+ if(w != nil)
+ *w = logiw;
+ if(h != nil)
+ *h = logih;
+
+ return 0;
+}
+
+void
+SDL_SetWindowSize(SDL_Window *, int w, int h)
+{
+ int f, n;
+
+ if(physw != w || physh != h){
+ physw = w;
+ physh = h;
+ if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){
+ n = fprint(f, "resize -dx %d -dy %d", w+Borderwidth*2, h+Borderwidth*2);
+ close(f);
+ if(n > 0){
+ getwindow(display, Refnone);
+ forceredraw = 1;
+ }
+ }
+ }
+}
+
+int
+SDL_ShowSimpleMessageBox(Uint32, char *title, char *message, SDL_Window *)
+{
+ /* FIXME display a GUI window? */
+ fprint(2, "%s: %s\n", title, message);
+ return 0;
+}
+
+int
+SDL_SetWindowFullscreen(SDL_Window *, Uint32)
+{
+ /* FIXME again, ft2 does NOT check the error code, figure something out */
+ werrstr("SDL_SetWindowFullscreen: not implemented");
+ return -1;
+}
+
+void
+SDL_SetWindowGrab(SDL_Window *, SDL_bool grabbed)
+{
+ /* FIXME not sure if it's worth anything */
+ USED(grabbed);
+}
+
+void
+SDL_SetWindowPosition(SDL_Window *, int x, int y)
+{
+ int f, n;
+
+ if((f = open("/dev/wctl", OWRITE|OCEXEC)) >= 0){
+ n = fprint(f, "move -minx %d -miny %d", x, y);
+ close(f);
+ if(n > 0){
+ getwindow(display, Refnone);
+ forceredraw = 1;
+ }
+ }
+}
+
+int
+SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode *mode)
+{
+ if(displayIndex != 0)
+ return -1;
+
+ mode->w = physw;
+ mode->h = physh;
+ mode->format = SDL_PIXELFORMAT_ARGB8888;
+ mode->refresh_rate = 0;
+
+ return 0;
+}
+
+void
+SDL_SetWindowTitle(SDL_Window *, char *title)
+{
+ int f;
+
+ if((f = open("/dev/label", OWRITE|OTRUNC|OCEXEC)) >= 0 || (f = open("/mnt/term/dev/label", OWRITE|OTRUNC|OCEXEC)) >= 0){
+ write(f, title, strlen(title));
+ close(f);
+ }
+}
+
+void
+SDL_DestroyTexture(SDL_Texture *t)
+{
+ freememimage(t);
+}
+
+int
+SDL_SetTextureBlendMode(SDL_Texture *, SDL_BlendMode blendMode)
+{
+ if(blendMode != SDL_BLENDMODE_NONE){
+ werrstr("SDL_SetTextureBlendMode: only SDL_BLENDMODE_NONE is supported");
+ return -1;
+ }
+
+ return 0;
+}
+
+SDL_bool
+SDL_SetHint(char *name, char *value)
+{
+ /* FIXME anyone cares about name="SDL_RENDER_SCALE_QUALITY" value="(best|nearest)"? */
+ USED(name);
+ USED(value);
+
+ return SDL_FALSE;
+}
+
+SDL_Window *
+SDL_CreateWindow(char *title, int x, int y, int w, int h, Uint32)
+{
+ SDL_SetWindowTitle(&onewin, title);
+ SDL_SetWindowSize(&onewin, w, h);
+
+ if(x != SDL_WINDOWPOS_UNDEFINED && y != SDL_WINDOWPOS_UNDEFINED){ /* FIXME either of these can be undefined */
+ if(x == SDL_WINDOWPOS_CENTERED)
+ x = display->image->r.min.x + (Dx(display->image->r) - w)/2;
+ if(y == SDL_WINDOWPOS_CENTERED)
+ y = display->image->r.min.y + (Dx(display->image->r) - w)/2;
+ SDL_SetWindowPosition(&onewin, x, y);
+ }
+
+ return &onewin;
+}
+
+SDL_Renderer *
+SDL_CreateRenderer(SDL_Window *, int, Uint32)
+{
+ SDL_RenderSetLogicalSize(nil, physw, physh);
+ return &oneren;
+}
+
+int
+SDL_SetRenderDrawBlendMode(SDL_Renderer *, SDL_BlendMode blendMode)
+{
+ if(blendMode != SDL_BLENDMODE_NONE){
+ werrstr("SDL_SetRenderDrawBlendMode: only SDL_BLENDMODE_NONE is supported");
+ return -1;
+ }
+
+ return 0;
+}
+
+char *
+SDL_GetCurrentVideoDriver(void)
+{
+ return "/dev/draw";
+}
+
+Uint32
+SDL_GetTicks(void)
+{
+ return nanosec()/1000000ULL;
+}
+
+SDL_bool
+SDL_HasClipboardText(void)
+{
+ /* most def */
+ return SDL_TRUE;
+}
+
+void
+SDL_SetThreadPriority(int)
+{
+ /* nothing to do here */
+}
+
+void
+SDL_RestoreWindow(SDL_Window *)
+{
+ /* nothing to do here */
+}
+
+void
+SDL_RaiseWindow(SDL_Window *)
+{
+ /* nothing to do here */
+}
+
+int
+SDL_SetSurfaceRLE(SDL_Surface *, int)
+{
+ /* nothing to do here */
+ return 0;
+}
+
+int
+SDL_SetSurfaceBlendMode(SDL_Surface *, SDL_BlendMode blendMode)
+{
+ if(blendMode != SDL_BLENDMODE_NONE){
+ werrstr("SDL_SetSurfaceBlendMode: only SDL_BLENDMODE_NONE is supported");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+SDL_LockSurface(SDL_Surface *)
+{
+ /* nothing to do here */
+ return 0;
+}
+
+int
+SDL_UnlockSurface(SDL_Surface *)
+{
+ /* nothing to do here */
+ return 0;
+}
+
+void
+SDL_DestroyRenderer(SDL_Renderer *)
+{
+ /* nothing to do here */
+}
+
+void
+SDL_DestroyWindow(SDL_Window *)
+{
+}
+
+SDL_bool
+SDL_HasSSE(void)
+{
+ /* it's not like we have builtins anyway */
+ return SDL_FALSE;
+}
+
+SDL_bool
+SDL_HasSSE2(void)
+{
+ /* it's not like we have builtins anyway */
+ return SDL_FALSE;
+}
+
+void
+SDL_EnableScreenSaver(void)
+{
+}
+
+int
+SDL_SaveBMP(SDL_Surface *s, const char *file)
+{
+ /* FIXME implement this */
+ USED(s, file);
+
+ return -1;
+}
--- /dev/null
+++ b/signal.h
@@ -1,0 +1,25 @@
+#ifndef _signal_h_
+#define _signal_h_
+
+#include "plan9.h"
+
+enum {
+ SA_RESETHAND = 1<<0, /* not used */
+
+ /* all are treated as SIGSEGV */
+ SIGILL = 4,
+ SIGABRT = 6,
+ SIGFPE = 8,
+ SIGSEGV = 11,
+};
+
+typedef void (*sa_handler)(int);
+
+struct sigaction {
+ sa_handler sa_handler;
+ int sa_flags;
+};
+
+void sigaction(int, struct sigaction *, struct sigaction *);
+
+#endif
--- /dev/null
+++ b/stdarg.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/stdbool.h
@@ -1,0 +1,6 @@
+#ifndef _stdbool_h_
+#define _stdbool_h_
+
+typedef enum { false, true } bool;
+
+#endif
--- /dev/null
+++ b/stddef.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/stdint.h
@@ -1,0 +1,21 @@
+#ifndef _stdint_h_
+#define _stdint_h_
+
+#include "plan9.h"
+#include <limits.h>
+
+typedef s8int int8_t;
+typedef u8int uint8_t;
+typedef s16int int16_t;
+typedef u16int uint16_t;
+typedef s32int int32_t;
+typedef u32int uint32_t;
+typedef s64int int64_t;
+typedef u64int uint64_t;
+typedef long ssize_t;
+typedef ulong size_t;
+typedef intptr ptrdiff_t;
+typedef intptr intptr_t;
+typedef uintptr uintptr_t;
+
+#endif
--- /dev/null
+++ b/stdio.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/stdlib.h
@@ -1,0 +1,8 @@
+#ifndef _stdlib_h_
+#define _stdlib_h_
+
+#include "plan9.h"
+
+#define exit(x) exits(x == 0 ? nil : "error")
+
+#endif
--- /dev/null
+++ b/string.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/strings.h
@@ -1,0 +1,7 @@
+#ifndef _strings_h_
+#define _strings_h_
+
+#define strcasecmp cistrcmp
+#define strncasecmp cistrncmp
+
+#endif
--- /dev/null
+++ b/sys/stat.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/sys/time.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/sys/types.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/time.h
@@ -1,0 +1,1 @@
+#include "plan9.h"
--- /dev/null
+++ b/unistd.c
@@ -1,0 +1,65 @@
+#include <unistd.h>
+
+#undef getenv
+#undef stat
+
+char *
+getenv9(char *s)
+{
+ static char t[1024];
+
+ snprint(t, sizeof(t), "%s", s = getenv(strcmp(s, "HOME") == 0 ? "home" : s));
+ free(s);
+
+ return t;
+}
+
+int
+stat9(char *filename, struct stat9 *buf)
+{
+ Dir *d;
+
+ if((d = dirstat(filename)) == nil)
+ return -1;
+
+ buf->st_size = d->length;
+ buf->st_mode = d->mode;
+ free(d);
+
+ return 0;
+}
+
+int
+mkdir(char *path, int perm)
+{
+ int f;
+
+ if(access(path, AEXIST) == 0){
+ werrstr("%s: already exists", path);
+ return -1;
+ }
+ if((f = create(path, OREAD, DMDIR|perm)) < 0){
+ werrstr("%s: can't create: %r", path);
+ return -1;
+ }
+ close(f);
+
+ return 0;
+}
+
+int
+rename(char *old, char *new)
+{
+ Dir d;
+
+ nulldir(&d);
+ d.name = new;
+
+ return dirwstat(old, &d) > 0 ? 0 : -1;
+}
+
+void
+usleep(unsigned us)
+{
+ nsleep((uvlong)us*1000ULL);
+}
--- /dev/null
+++ b/unistd.h
@@ -1,0 +1,47 @@
+#ifndef _unistd_h_
+#define _unistd_h_
+
+#include "plan9.h"
+
+enum {
+ S_IFMT = 0xff,
+ S_IFDIR = 1<<0,
+
+ F_OK = AEXIST,
+ R_OK = AREAD,
+ W_OK = AWRITE,
+ X_OK = AEXEC,
+
+ S_ISUID = 04000,
+ S_ISGID = 02000,
+ S_IRWXU = 00700,
+ S_IRUSR = 00400,
+ S_IWUSR = 00200,
+ S_IXUSR = 00100,
+ S_IRWXG = 00070,
+ S_IRGRP = 00040,
+ S_IWGRP = 00020,
+ S_IXGRP = 00010,
+ S_IRWXO = 00007,
+ S_IROTH = 00004,
+ S_IWOTH = 00002,
+ S_IXOTH = 00001,
+};
+
+#define getcwd getwd
+#define getenv getenv9
+#define stat stat9
+
+struct stat9 {
+ uvlong st_size;
+ int st_mode;
+};
+
+char *getenv9(char *s);
+int stat9(char *filename, struct stat9 *buf);
+int mkdir(char *path, int perm);
+int access(char *name, int mode);
+int rename(char *old, char *new);
+void usleep(unsigned us);
+
+#endif
--- /dev/null
+++ b/wchar.h
@@ -1,0 +1,1 @@
+#include "plan9.h"