reinit; VM and 9p core mostly done
This commit is contained in:
commit
6fa747864e
11 changed files with 581 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
kuro
|
||||||
|
kurosrvr
|
7
build
Executable file
7
build
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/rc
|
||||||
|
|
||||||
|
mk
|
||||||
|
|
||||||
|
mv 6.out kurosrvr
|
||||||
|
cp kurosrvr kuro
|
||||||
|
rm *.6
|
35
client.c
Normal file
35
client.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
static char mtpt[256] = {0};
|
||||||
|
|
||||||
|
void kuro9p_set_mtpt(char* path) {
|
||||||
|
strcpy(mtpt, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kuro9p_write(char* path, char* data, int len) {
|
||||||
|
int fd;
|
||||||
|
char fullpath[256];
|
||||||
|
strcpy(fullpath, mtpt);
|
||||||
|
strcat(fullpath, path);
|
||||||
|
|
||||||
|
fd = open(fullpath, OWRITE);
|
||||||
|
if (fd >= 0) {
|
||||||
|
write(fd, data, len);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* kuro9p_read(char* path, char* buf, int len) {
|
||||||
|
int fd;
|
||||||
|
char fullpath[256];
|
||||||
|
strcpy(fullpath, mtpt);
|
||||||
|
strcat(fullpath, path);
|
||||||
|
|
||||||
|
fd = open(fullpath, OREAD);
|
||||||
|
if (fd >= 0) {
|
||||||
|
read(fd, buf, len);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
102
dat.h
Normal file
102
dat.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcall.h>
|
||||||
|
#include <thread.h>
|
||||||
|
#include <9p.h>
|
||||||
|
#include <draw.h>
|
||||||
|
#include <cursor.h>
|
||||||
|
#include <mouse.h>
|
||||||
|
#include <keyboard.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CTL = 1
|
||||||
|
} FileType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KURO_RUNS = 0,
|
||||||
|
KURO_QUITS
|
||||||
|
} WindowStatus;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PORT_MOUSE,
|
||||||
|
PORT_RESIZE,
|
||||||
|
PORT_KBD,
|
||||||
|
PORT_STATUS,
|
||||||
|
TOTAL_PORTS
|
||||||
|
} Port;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INIT,
|
||||||
|
OPEN_FILE,
|
||||||
|
SAVE_FILE,
|
||||||
|
MOUSE,
|
||||||
|
KEYBOARD,
|
||||||
|
SCROLL,
|
||||||
|
CURSOR,
|
||||||
|
SELECT,
|
||||||
|
INSERT,
|
||||||
|
DELETE,
|
||||||
|
CUT,
|
||||||
|
COPY,
|
||||||
|
PASTE,
|
||||||
|
EXEC,
|
||||||
|
PLUMB,
|
||||||
|
INDICATE,
|
||||||
|
SPLIT,
|
||||||
|
PARTITION,
|
||||||
|
TOTAL_OPCODES
|
||||||
|
} Opcode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TAGF,
|
||||||
|
BODYF,
|
||||||
|
SHELLF,
|
||||||
|
TOTAL_SUBF
|
||||||
|
} SubFrame;
|
||||||
|
|
||||||
|
typedef struct Aux {
|
||||||
|
FileType type;
|
||||||
|
char* data;
|
||||||
|
int count;
|
||||||
|
} Aux;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Opcode opcode;
|
||||||
|
void* data;
|
||||||
|
} Instruction;
|
||||||
|
|
||||||
|
typedef void (*Handler)(void*, void*);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uvlong id;
|
||||||
|
WindowStatus status;
|
||||||
|
Image* img;
|
||||||
|
Screen* screen;
|
||||||
|
char filepath[512];
|
||||||
|
Rune* tag;
|
||||||
|
uint tag_len;
|
||||||
|
Rune* body;
|
||||||
|
uint body_len;
|
||||||
|
} KuroMemory;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uvlong id;
|
||||||
|
int fd;
|
||||||
|
Channel* cpu; /* chan(Instruction) */
|
||||||
|
Handler* handlers;
|
||||||
|
KuroMemory* memory;
|
||||||
|
Channel* status; /* chan(WidowStatus) */
|
||||||
|
} Node;
|
||||||
|
|
||||||
|
typedef struct NodeRef NodeRef;
|
||||||
|
|
||||||
|
struct NodeRef {
|
||||||
|
int fd[2];
|
||||||
|
uvlong id;
|
||||||
|
NodeRef* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NodeRef* data[256];
|
||||||
|
} NodeTable;
|
20
fns.h
Normal file
20
fns.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
int strequ(char* s1, char* s2);
|
||||||
|
char* strcsw(char* s, char a, char b);
|
||||||
|
char* uvlong_to_hex(uvlong input, char* buf);
|
||||||
|
|
||||||
|
void start_9p(char* mtpt);
|
||||||
|
|
||||||
|
void kuro9p_set_mtpt(char* mtpt);
|
||||||
|
void kuro9p_write(char* path, char* data, int len);
|
||||||
|
char* kuro9p_read(char* path, char* buf, int len);
|
||||||
|
|
||||||
|
void nodetbl_add(NodeTable* self, NodeRef* node);
|
||||||
|
void nodetbl_del(NodeTable* self, uvlong id);
|
||||||
|
|
||||||
|
void node_setup(Node* self, uvlong id, int fd, Handler* handlers, KuroMemory* memory);
|
||||||
|
void node_execute(Node* self);
|
||||||
|
void node_loop(void* data);
|
||||||
|
Node* create_node(uvlong id, int fd, int new, char* filename);
|
||||||
|
void supervise_node(Node* self);
|
||||||
|
void node_cleanup(Node* self);
|
||||||
|
void memory_cleanup(KuroMemory* self);
|
75
kuro.c
Normal file
75
kuro.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
static char* mtpt;
|
||||||
|
|
||||||
|
int threadmaybackground(void) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void threadmain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
/* we want to strip any path elements off the executable name */
|
||||||
|
char exe_name[256] = {0};
|
||||||
|
char* exe_base;
|
||||||
|
|
||||||
|
uvlong node_id = 0;
|
||||||
|
int client_fd = -1;
|
||||||
|
int newwin = 0;
|
||||||
|
|
||||||
|
char client_9p_str[256] = {0};
|
||||||
|
|
||||||
|
strcpy(exe_name, argv[0]);
|
||||||
|
exe_base = utfrrune(exe_name, '/');
|
||||||
|
if (exe_base) {
|
||||||
|
exe_base++;
|
||||||
|
} else {
|
||||||
|
exe_base = exe_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we are the server, serve the filetree on 9p */
|
||||||
|
|
||||||
|
if (strequ(exe_base, "kurosrvr")) {
|
||||||
|
if (argc > 1) {
|
||||||
|
start_9p(argv[1]);
|
||||||
|
} else {
|
||||||
|
sysfatal("usage: kurosrvr mtpt");
|
||||||
|
}
|
||||||
|
} else if (strequ(exe_base, "kuro")) {
|
||||||
|
mtpt = getenv("KURO_MTPT");
|
||||||
|
if (mtpt == nil) {
|
||||||
|
sysfatal("KURO_MTPT not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
kuro9p_set_mtpt(mtpt);
|
||||||
|
print("KURO_MTPT=%s\n", mtpt);
|
||||||
|
|
||||||
|
ARGBEGIN{
|
||||||
|
case 'n':
|
||||||
|
newwin=1;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
sscanf(ARGF(), "%d", &node_id);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
sscanf(ARGF(), "%d", &client_fd);
|
||||||
|
break;
|
||||||
|
}ARGEND
|
||||||
|
|
||||||
|
strcpy(client_9p_str, "new");
|
||||||
|
if (argv[0]) {
|
||||||
|
strcat(client_9p_str, " ");
|
||||||
|
strcat(client_9p_str, argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node_id > 0 && client_fd >= 0) {
|
||||||
|
supervise_node(create_node(node_id, client_fd, newwin, argv[0]));
|
||||||
|
} else {
|
||||||
|
/* if node_id or client_id are missing, ask the server to fork us with them, and forward the filename */
|
||||||
|
print("9pstr=%s\n", client_9p_str);
|
||||||
|
kuro9p_write("/ctl", client_9p_str, strlen(client_9p_str));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sysfatal("invoke as kurosrvr to start the background service or kuro to start an application window");
|
||||||
|
}
|
||||||
|
}
|
24
mkfile
Normal file
24
mkfile
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
</$objtype/mkfile
|
||||||
|
BIN=/$objtype/bin
|
||||||
|
|
||||||
|
TARG=kuro
|
||||||
|
|
||||||
|
OFILES=\
|
||||||
|
kuro.$O\
|
||||||
|
srv.$O\
|
||||||
|
client.$O\
|
||||||
|
util.$O\
|
||||||
|
node.$O\
|
||||||
|
nodetable.$O\
|
||||||
|
|
||||||
|
HFILES=dat.h\
|
||||||
|
fns.h\
|
||||||
|
|
||||||
|
UPDATE=\
|
||||||
|
mkfile\
|
||||||
|
$HFILES\
|
||||||
|
${OFILES:%.$O=%.c}\
|
||||||
|
|
||||||
|
</sys/src/cmd/mkone
|
||||||
|
|
||||||
|
$O.out: /$objtype/lib/libframe.a /$objtype/lib/libdraw.a /$objtype/lib/libthread.a
|
132
node.c
Normal file
132
node.c
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
|
||||||
|
void node_setup(Node* self, uvlong id, int fd, Handler* handlers, KuroMemory* memory) {
|
||||||
|
if (self) {
|
||||||
|
self->id = id;
|
||||||
|
self->fd = fd;
|
||||||
|
self->handlers = handlers;
|
||||||
|
self->memory = memory;
|
||||||
|
self->cpu = chancreate(sizeof(Instruction), 0);
|
||||||
|
self->status = chancreate(sizeof(WindowStatus), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void node_loop(void* data) {
|
||||||
|
Node* self = (Node*)data;
|
||||||
|
Instruction x;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
recv(self->cpu, &x);
|
||||||
|
if (self->handlers[x.opcode]) {
|
||||||
|
(*(self->handlers[x.opcode]))(self, x.data);
|
||||||
|
switch(self->memory->status) {
|
||||||
|
case KURO_QUITS:
|
||||||
|
nbsend(self->status, 0);
|
||||||
|
threadexits(0);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
flushimage(self->memory->screen->display, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_execute(Node* self) {
|
||||||
|
threadcreate(node_loop, self, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* create_node(uvlong id, int fd, int new, char* filename) {
|
||||||
|
|
||||||
|
if (new) {
|
||||||
|
newwindow("-dx 600 -dy 600");
|
||||||
|
}
|
||||||
|
initdraw(nil, nil, "kuro");
|
||||||
|
|
||||||
|
KuroMemory* self = (KuroMemory*)malloc(sizeof(KuroMemory));
|
||||||
|
Node* node = (Node*)malloc(sizeof(Node));
|
||||||
|
Handler* handlers = (Handler*)malloc(TOTAL_OPCODES * sizeof(Handler*));
|
||||||
|
|
||||||
|
node_setup(node, id, fd, handlers, self);
|
||||||
|
|
||||||
|
self->img = screen;
|
||||||
|
self->screen = _screen;
|
||||||
|
|
||||||
|
strcpy(self->filepath, filename);
|
||||||
|
|
||||||
|
/* node_execute runs the node on a separate thread */
|
||||||
|
node_execute(node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void supervise_node(Node* self) {
|
||||||
|
|
||||||
|
Mousectl* mctl;
|
||||||
|
Keyboardctl* kctl;
|
||||||
|
Mouse mouse;
|
||||||
|
int resize[2];
|
||||||
|
WindowStatus status;
|
||||||
|
Rune kbd;
|
||||||
|
|
||||||
|
if ((mctl = initmouse(nil, screen)) == nil) {
|
||||||
|
sysfatal("couldn't initialize mctl");
|
||||||
|
}
|
||||||
|
if ((kctl = initkeyboard(nil)) == nil) {
|
||||||
|
sysfatal("couldn't initialize kctl");
|
||||||
|
}
|
||||||
|
|
||||||
|
Alt alts[TOTAL_PORTS + 1] = {
|
||||||
|
{mctl->c, &mouse, CHANRCV},
|
||||||
|
{mctl->resizec, resize, CHANRCV},
|
||||||
|
{kctl->c, &kbd, CHANRCV},
|
||||||
|
{self->status, &status, CHANRCV},
|
||||||
|
{nil, nil, CHANEND}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
switch (alt(alts)) {
|
||||||
|
case PORT_MOUSE:
|
||||||
|
print("got a mouse event");
|
||||||
|
break;
|
||||||
|
case PORT_RESIZE:
|
||||||
|
lockdisplay(display);
|
||||||
|
if (getwindow(display, Refnone) < 0)
|
||||||
|
sysfatal("couldn't resize");
|
||||||
|
unlockdisplay(display);
|
||||||
|
print("got a resize event");
|
||||||
|
break;
|
||||||
|
case PORT_KBD:
|
||||||
|
print("got a keypress");
|
||||||
|
break;
|
||||||
|
case PORT_STATUS:
|
||||||
|
switch(status) {
|
||||||
|
case KURO_QUITS:
|
||||||
|
print("VM died - let's clean up the data");
|
||||||
|
goto cleanup;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cleanup:
|
||||||
|
node_cleanup(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_cleanup(Node* self) {
|
||||||
|
if (self) {
|
||||||
|
memory_cleanup(self->memory);
|
||||||
|
chanfree(self->cpu);
|
||||||
|
chanfree(self->status);
|
||||||
|
/* TODO: send message on self->fd to tell the server to remove us from NodeTable and filetree */
|
||||||
|
/* handlers array is shared between all the nodes */
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_cleanup(KuroMemory* self) {
|
||||||
|
if (self) {
|
||||||
|
/* do we need to free the img and screen? */
|
||||||
|
}
|
||||||
|
}
|
42
nodetable.c
Normal file
42
nodetable.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
void nodetbl_add(NodeTable* self, NodeRef* node) {
|
||||||
|
int i;
|
||||||
|
NodeRef* n;
|
||||||
|
if (self && node) {
|
||||||
|
i = (int)(node->id%256);
|
||||||
|
n = self->data[i];
|
||||||
|
if (n && n->next) {
|
||||||
|
n = n->next;
|
||||||
|
}
|
||||||
|
if (n) {
|
||||||
|
n->next = node;
|
||||||
|
} else {
|
||||||
|
n = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nodetbl_del(NodeTable* self, uvlong id) {
|
||||||
|
NodeRef* n;
|
||||||
|
NodeRef* nprev;
|
||||||
|
|
||||||
|
if (self) {
|
||||||
|
for(int i = 0; i <= 255; i++) {
|
||||||
|
n = self->data[i];
|
||||||
|
|
||||||
|
while (n && n->id != id && n->next) {
|
||||||
|
nprev = n;
|
||||||
|
n = n->next;
|
||||||
|
}
|
||||||
|
if (n->id == id) {
|
||||||
|
if (nprev) {
|
||||||
|
nprev->next = n->next;
|
||||||
|
}
|
||||||
|
free(n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
srv.c
Normal file
122
srv.c
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
static NodeTable nodes;
|
||||||
|
static QLock id_lock;
|
||||||
|
|
||||||
|
static uvlong get_next_id(void) {
|
||||||
|
static uvlong id = 0;
|
||||||
|
uvlong next;
|
||||||
|
qlock(&id_lock);
|
||||||
|
next = ++id;
|
||||||
|
qunlock(&id_lock);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aux* create_aux(FileType t) {
|
||||||
|
Aux* self = (Aux*)malloc(sizeof(Aux));
|
||||||
|
self->type = t;
|
||||||
|
self->data = nil;
|
||||||
|
self->count = 0;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_destroy_file(File* f) {
|
||||||
|
Aux* a = (Aux*)f->aux;
|
||||||
|
if(a && a->data) {
|
||||||
|
free(a->data);
|
||||||
|
}
|
||||||
|
if (a) {
|
||||||
|
free (a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kuro_read(Req* r) {
|
||||||
|
Aux* a = (Aux*)r->fid->file->aux;
|
||||||
|
switch (a->type) {
|
||||||
|
case CTL:
|
||||||
|
default:
|
||||||
|
respond(r, nil);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void new_window(char* filename, int new) {
|
||||||
|
int rw_fd[2];
|
||||||
|
char client_fd[32] = {0};
|
||||||
|
char client_id[32] = {0};
|
||||||
|
uvlong id;
|
||||||
|
NodeRef* noderef;
|
||||||
|
|
||||||
|
pipe(rw_fd);
|
||||||
|
id = get_next_id();
|
||||||
|
|
||||||
|
noderef = (NodeRef*)malloc(sizeof(NodeRef));
|
||||||
|
noderef->id = id;
|
||||||
|
noderef->fd[0] = rw_fd[0];
|
||||||
|
noderef->fd[1] = rw_fd[1];
|
||||||
|
nodetbl_add(&nodes, noderef);
|
||||||
|
|
||||||
|
sprintf(client_fd, "%d", rw_fd[1]);
|
||||||
|
sprintf(client_id, "%d", id);
|
||||||
|
|
||||||
|
rfork(RFNAMEG);
|
||||||
|
char* a[] = { "kuro", "-p", client_fd, "-i", client_id, new ? "-n" : filename, new ? filename : 0, 0 };
|
||||||
|
exec("./kuro", a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_ctl(Req* r) {
|
||||||
|
char cmd[16] = {0};
|
||||||
|
char* c = r->ifcall.data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) {
|
||||||
|
strncat(cmd, c, 1);
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*c == ' ') {
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* maybe don't do this... */
|
||||||
|
strcsw(c, '\n', 0);
|
||||||
|
|
||||||
|
/* diagnostics for now */
|
||||||
|
print("cmd: %s\n", cmd);
|
||||||
|
print("arg: %s\n", c);
|
||||||
|
|
||||||
|
if (strequ(cmd, "new")) {
|
||||||
|
new_window(c, 1);
|
||||||
|
respond(r, nil);
|
||||||
|
} else {
|
||||||
|
print("unknown command...\n");
|
||||||
|
respond(r, nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kuro_write(Req* r) {
|
||||||
|
Aux* a = (Aux*)r->fid->file->aux;
|
||||||
|
switch (a->type) {
|
||||||
|
case CTL:
|
||||||
|
write_ctl(r);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
respond(r, nil);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_9p(char* mtpt) {
|
||||||
|
if (mtpt) {
|
||||||
|
putenv("KURO_MTPT", mtpt);
|
||||||
|
Srv srv = { .read = kuro_read, .write = kuro_write };
|
||||||
|
Tree* tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file);
|
||||||
|
srv.tree = tree;
|
||||||
|
closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0600, create_aux(CTL)));
|
||||||
|
closefile(createfile(tree->root, "nodes", nil, DMDIR | 0600, nil));
|
||||||
|
closefile(tree->root);
|
||||||
|
/* TODO: figure out how to kill the server cleanly so we don't need MREPL */
|
||||||
|
threadpostmountsrv(&srv, nil, mtpt, MREPL | MCREATE);
|
||||||
|
}
|
||||||
|
}
|
20
util.c
Normal file
20
util.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
|
||||||
|
int strequ(char* s1, char* s2) {
|
||||||
|
return strcmp(s1, s2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* strcsw(char* s, char a, char b) {
|
||||||
|
int i = 0;
|
||||||
|
char c;
|
||||||
|
while ((c = s[i])) {
|
||||||
|
s[i++] = c == a ? b : c;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* uvlong_to_hex(uvlong input, char* buf) {
|
||||||
|
sprintf(buf, "%08%x\0", input);
|
||||||
|
return buf;
|
||||||
|
}
|
Loading…
Reference in a new issue