rename libc to libjehanne; improve libposix

This is to avoid conflicts between standards' assumptions and Jehanne's choices
This commit is contained in:
2020-01-11 16:28:11 +01:00
parent 3c7dc6eb8b
commit 2f850291d5
242 changed files with 39 additions and 39 deletions

1
sys/src/lib/jehanne/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
amd64/syscalls.c

View File

@@ -0,0 +1,60 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_access(const char *name, int mode)
{
int fd, reqmode;
static char omode[] = {
OSTAT,
OEXEC,
OWRITE,
ORDWR,
OREAD,
OEXEC, /* 5=4+1 READ|EXEC, EXEC is enough */
ORDWR,
ORDWR /* 7=4+2+1 READ|WRITE|EXEC, ignore EXEC */
};
reqmode = omode[mode&AMASK];
fd = sys_open(name, reqmode);
if(fd >= 0){
sys_close(fd);
return 0;
}
/* WARNING:
*
* In Plan 9 access(AWRITE) and access(AEXEC) in directories
* fail despite the actual permission of the directory.
*
* This is well understood in Plan 9, but it's counter intuitive.
*
* In Plan 9, to create a file in a directory you need write
* permission in the directory. Still you don't need to (and you
* cannot) open the directory for writing before calling create.
*
* To my eyes this is a UNIX inheritance that could be "fixed"
* but there are some trade off.
*/
return -1;
}

View File

@@ -0,0 +1,284 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <ctype.h>
static int nettrans(const char*, char*, int na, char*, int);
enum
{
Maxpath= 256,
};
/*
* announce a network service.
*/
int
jehanne_announce(const char *addr, char *dir)
{
int ctl, n, m;
char buf[Maxpath];
char buf2[Maxpath];
char netdir[Maxpath];
char naddr[Maxpath];
char *cp;
/*
* translate the address
*/
if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
return -1;
/*
* get a control channel
*/
ctl = sys_open(netdir, ORDWR);
if(ctl<0){
jehanne_werrstr("announce opening %s: %r", netdir);
return -1;
}
cp = jehanne_strrchr(netdir, '/');
if(cp == nil){
jehanne_werrstr("announce arg format %s", netdir);
sys_close(ctl);
return -1;
}
*cp = 0;
/*
* find out which line we have
*/
n = jehanne_snprint(buf, sizeof(buf), "%s/", netdir);
m = jehanne_read(ctl, &buf[n], sizeof(buf)-n-1);
if(m <= 0){
jehanne_werrstr("announce reading %s: %r", netdir);
sys_close(ctl);
return -1;
}
buf[n+m] = 0;
/*
* make the call
*/
n = jehanne_snprint(buf2, sizeof(buf2), "announce %s", naddr);
if(jehanne_write(ctl, buf2, n)!=n){
jehanne_werrstr("announce writing %s: %r", netdir);
sys_close(ctl);
return -1;
}
/*
* return directory etc.
*/
if(dir){
jehanne_strncpy(dir, buf, NETPATHLEN);
dir[NETPATHLEN-1] = 0;
}
return ctl;
}
/*
* listen for an incoming call
*/
int
jehanne_listen(const char *dir, char *newdir)
{
int ctl, n, m;
char buf[Maxpath];
char *cp;
/*
* open listen, wait for a call
*/
jehanne_snprint(buf, sizeof(buf), "%s/listen", dir);
ctl = sys_open(buf, ORDWR);
if(ctl < 0){
jehanne_werrstr("listen opening %s: %r", buf);
return -1;
}
/*
* find out which line we have
*/
jehanne_strncpy(buf, dir, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
cp = jehanne_strrchr(buf, '/');
if(cp == nil){
sys_close(ctl);
jehanne_werrstr("listen arg format %s", dir);
return -1;
}
*++cp = 0;
n = cp-buf;
m = jehanne_read(ctl, cp, sizeof(buf) - n - 1);
if(m <= 0){
sys_close(ctl);
jehanne_werrstr("listen reading %s/listen: %r", dir);
return -1;
}
buf[n+m] = 0;
/*
* return directory etc.
*/
if(newdir){
jehanne_strncpy(newdir, buf, NETPATHLEN);
newdir[NETPATHLEN-1] = 0;
}
return ctl;
}
/*
* accept a call, return an fd to the open data file
*/
int
jehanne_accept(int ctl, const char *dir)
{
char buf[Maxpath];
const char *num;
int32_t n;
num = jehanne_strrchr(dir, '/');
if(num == nil)
num = dir;
else
num++;
n = jehanne_snprint(buf, sizeof(buf), "accept %s", num);
jehanne_write(ctl, buf, n); /* ignore return value, network might not need accepts */
jehanne_snprint(buf, sizeof(buf), "%s/data", dir);
return sys_open(buf, ORDWR);
}
/*
* reject a call, tell device the reason for the rejection
*/
int
jehanne_reject(int ctl, const char *dir, const char *cause)
{
char buf[Maxpath];
const char *num;
int32_t n;
num = jehanne_strrchr(dir, '/');
if(num == 0)
num = dir;
else
num++;
jehanne_snprint(buf, sizeof(buf), "reject %s %s", num, cause);
n = jehanne_strlen(buf);
if(jehanne_write(ctl, buf, n) != n)
return -1;
return 0;
}
/*
* perform the identity translation (in case we can't reach cs)
*/
static int
identtrans(char *netdir, const char *addr, char *naddr, int na,
char *file, int nf)
{
char proto[Maxpath];
char *p;
USED(nf);
/* parse the protocol */
jehanne_strncpy(proto, addr, sizeof(proto));
proto[sizeof(proto)-1] = 0;
p = jehanne_strchr(proto, '!');
if(p)
*p++ = 0;
jehanne_snprint(file, nf, "%s/%s/clone", netdir, proto);
jehanne_strncpy(naddr, p, na);
naddr[na-1] = 0;
return 1;
}
/*
* call up the connection server and get a translation
*/
static int
nettrans(const char *addr, char *naddr, int na, char *file, int nf)
{
int i, fd;
char buf[Maxpath];
char netdir[Maxpath];
char *p, *p2;
int32_t n;
/*
* parse, get network directory
*/
p = jehanne_strchr(addr, '!');
if(p == 0){
jehanne_werrstr("bad dial string: %s", addr);
return -1;
}
if(*addr != '/'){
jehanne_strncpy(netdir, "/net", sizeof(netdir));
netdir[sizeof(netdir) - 1] = 0;
} else {
for(p2 = p; *p2 != '/'; p2--)
;
i = p2 - addr;
if(i == 0 || i >= sizeof(netdir)){
jehanne_werrstr("bad dial string: %s", addr);
return -1;
}
jehanne_strncpy(netdir, addr, i);
netdir[i] = 0;
addr = p2 + 1;
}
/*
* ask the connection server
*/
jehanne_snprint(buf, sizeof(buf), "%s/cs", netdir);
fd = sys_open(buf, ORDWR);
if(fd < 0)
return identtrans(netdir, addr, naddr, na, file, nf);
if(jehanne_write(fd, addr, jehanne_strlen(addr)) < 0){
sys_close(fd);
return -1;
}
sys_seek(fd, 0, 0);
n = jehanne_read(fd, buf, sizeof(buf)-1);
sys_close(fd);
if(n <= 0)
return -1;
buf[n] = 0;
/*
* parse the reply
*/
p = jehanne_strchr(buf, ' ');
if(p == 0)
return -1;
*p++ = 0;
jehanne_strncpy(naddr, p, na);
naddr[na-1] = 0;
if(buf[0] == '/'){
p = jehanne_strchr(buf+1, '/');
if(p == nil)
p = buf;
else
p++;
}
jehanne_snprint(file, nf, "%s/%s", netdir, p);
return 0;
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_awakened(int64_t wakeup)
{
/* awake returns the ticks of the scheduled wakeup in negative,
* thus a wakeup is in the past iff (-sys_awake(0)) >= (-wakeup)
*
* NOTE: this is not a macro so that we can change the sys_awake()
* implementation in the future, without affecting the client code.
*/
assert(wakeup < 0);
return wakeup >= sys_awake(0);
}
int
jehanne_forgivewkp(int64_t wakeup)
{
/* awake returns the ticks of the scheduled wakeup in negative,
* and is able to remove a wakeup provided such value.
*
* jehanne_forgivewkp() is just a wrapper to hide sys_awake()'s details that
* could change in the future and make client code easier to
* read.
*
* NOTE: this is not a macro so that we can change the sys_awake()
* implementation in the future, without affecting the client code.
*/
assert(wakeup < 0);
return sys_awake(wakeup);
}

View File

@@ -0,0 +1,38 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_chdir(const char *dirname)
{
char buf[32];
int tmp, fd;
tmp = jehanne_getpid();
jehanne_snprint(buf, sizeof(buf), "/proc/%d/wdir", tmp);
fd = sys_open(buf, OWRITE);
if(fd < 0)
fd = sys_open("#0/wdir", OWRITE);
if(fd < 0)
return fd;
tmp = jehanne_write(fd, dirname, 1+jehanne_strlen(dirname));
sys_close(fd);
return tmp;
}

View File

@@ -0,0 +1,104 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
uint32_t
jehanne_sizeD2M(Dir *d)
{
char *sv[4];
int i, ns;
sv[0] = d->name;
sv[1] = d->uid;
sv[2] = d->gid;
sv[3] = d->muid;
ns = 0;
for(i = 0; i < 4; i++)
if(sv[i])
ns += jehanne_strlen(sv[i]);
return STATFIXLEN + ns;
}
uint32_t
jehanne_convD2M(Dir *d, uint8_t *buf, uint32_t nbuf)
{
uint8_t *p, *ebuf;
char *sv[4];
int i, ns, nsv[4], ss;
if(nbuf < BIT16SZ)
return 0;
p = buf;
ebuf = buf + nbuf;
sv[0] = d->name;
sv[1] = d->uid;
sv[2] = d->gid;
sv[3] = d->muid;
ns = 0;
for(i = 0; i < 4; i++){
if(sv[i])
nsv[i] = jehanne_strlen(sv[i]);
else
nsv[i] = 0;
ns += nsv[i];
}
ss = STATFIXLEN + ns;
/* set size before erroring, so user can know how much is needed */
/* note that length excludes count field itself */
PBIT16(p, ss-BIT16SZ);
p += BIT16SZ;
if(ss > nbuf)
return BIT16SZ;
PBIT16(p, d->type);
p += BIT16SZ;
PBIT32(p, d->dev);
p += BIT32SZ;
PBIT8(p, d->qid.type);
p += BIT8SZ;
PBIT32(p, d->qid.vers);
p += BIT32SZ;
PBIT64(p, d->qid.path);
p += BIT64SZ;
PBIT32(p, d->mode);
p += BIT32SZ;
PBIT32(p, d->atime);
p += BIT32SZ;
PBIT32(p, d->mtime);
p += BIT32SZ;
PBIT64(p, d->length);
p += BIT64SZ;
for(i = 0; i < 4; i++){
ns = nsv[i];
if(p + ns + BIT16SZ > ebuf)
return 0;
PBIT16(p, ns);
p += BIT16SZ;
if(ns)
jehanne_memmove(p, sv[i], ns);
p += ns;
}
if(ss != p - buf)
return 0;
return p - buf;
}

View File

@@ -0,0 +1,105 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
int
statcheck(uint8_t *buf, uint32_t nbuf)
{
uint8_t *ebuf;
int i;
ebuf = buf + nbuf;
if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
return -1;
buf += STATFIXLEN - 4 * BIT16SZ;
for(i = 0; i < 4; i++){
if(buf + BIT16SZ > ebuf)
return -1;
buf += BIT16SZ + GBIT16(buf);
}
if(buf != ebuf)
return -1;
return 0;
}
static char nullstring[] = "";
uint32_t
jehanne_convM2D(uint8_t *buf, uint32_t nbuf, Dir *d, char *strs)
{
uint8_t *p, *ebuf;
char *sv[4];
int i, ns;
if(nbuf < STATFIXLEN)
return 0;
p = buf;
ebuf = buf + nbuf;
p += BIT16SZ; /* ignore size */
d->type = GBIT16(p);
p += BIT16SZ;
d->dev = GBIT32(p);
p += BIT32SZ;
d->qid.type = GBIT8(p);
p += BIT8SZ;
d->qid.vers = GBIT32(p);
p += BIT32SZ;
d->qid.path = GBIT64(p);
p += BIT64SZ;
d->mode = GBIT32(p);
p += BIT32SZ;
d->atime = GBIT32(p);
p += BIT32SZ;
d->mtime = GBIT32(p);
p += BIT32SZ;
d->length = GBIT64(p);
p += BIT64SZ;
for(i = 0; i < 4; i++){
if(p + BIT16SZ > ebuf)
return 0;
ns = GBIT16(p);
if(ns < 0)
return 0;
p += BIT16SZ;
if(p + ns > ebuf)
return 0;
if(strs){
sv[i] = strs;
jehanne_memmove(strs, p, ns);
strs += ns;
*strs++ = '\0';
}
p += ns;
}
if(strs){
d->name = sv[0];
d->uid = sv[1];
d->gid = sv[2];
d->muid = sv[3];
}else{
d->name = nullstring;
d->uid = nullstring;
d->gid = nullstring;
d->muid = nullstring;
}
return p - buf;
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#define HZ 1000
double
jehanne_cputime(void)
{
int32_t t[4];
int i;
jehanne_times(t);
for(i=1; i<4; i++)
t[0] += t[i];
return t[0] / (double)HZ;
}

View File

@@ -0,0 +1,316 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* This routine converts time as follows.
* The epoch is 0000 Jan 1 1970 GMT.
* The argument time is in seconds since then.
* The jehanne_localtime(t) entry returns a pointer to an array
* containing
*
* seconds (0-59)
* minutes (0-59)
* hours (0-23)
* day of month (1-31)
* month (0-11)
* year-1970
* weekday (0-6, Sun is 0)
* day of the year
* daylight savings flag
*
* The routine gets the daylight savings time from the environment.
*
* jehanne_asctime(tvec))
* where tvec is produced by localtime
* returns a ptr to a character string
* that has the ascii time in the form
*
* \\
* Thu Jan 01 00:00:00 GMT 1970n0
* 012345678901234567890123456789
* 0 1 2
*
* jehanne_ctime(t) just calls localtime, then asctime.
*/
#include <u.h>
#include <libc.h>
static char dmsize[12] =
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/*
* The following table is used for 1974 and 1975 and
* gives the day number of the first day after the Sunday of the
* change.
*/
static int dysize(int);
static void ct_numb(char*, int);
#define TZSIZE 150
static void readtimezone(void);
static int rd_name(char**, char*);
static int rd_long(char**, int32_t*);
static
struct
{
char stname[4];
char dlname[4];
int32_t stdiff;
int32_t dldiff;
int32_t dlpairs[TZSIZE];
} timezone;
char*
jehanne_ctime(int32_t t)
{
return jehanne_asctime(jehanne_localtime(t));
}
Tm*
jehanne_localtime(int32_t tim)
{
Tm *ct;
int32_t t, *p;
int dlflag;
if(timezone.stname[0] == 0)
readtimezone();
t = tim + timezone.stdiff;
dlflag = 0;
for(p = timezone.dlpairs; *p; p += 2)
if(t >= p[0])
if(t < p[1]) {
t = tim + timezone.dldiff;
dlflag++;
break;
}
ct = jehanne_gmtime(t);
if(dlflag){
jehanne_strcpy(ct->zone, timezone.dlname);
ct->tzoff = timezone.dldiff;
} else {
jehanne_strcpy(ct->zone, timezone.stname);
ct->tzoff = timezone.stdiff;
}
return ct;
}
Tm*
jehanne_gmtime(int32_t tim)
{
int d0, d1;
int32_t hms, day;
static Tm xtime;
/*
* break initial number into days
*/
hms = (uint32_t)tim % 86400L;
day = (uint32_t)tim / 86400L;
if(hms < 0) {
hms += 86400L;
day -= 1;
}
/*
* generate hours:minutes:seconds
*/
xtime.sec = hms % 60;
d1 = hms / 60;
xtime.min = d1 % 60;
d1 /= 60;
xtime.hour = d1;
/*
* day is the day number.
* generate day of the week.
* The addend is 4 mod 7 (1/1/1970 was Thursday)
*/
xtime.wday = (day + 7340036L) % 7;
/*
* year number
*/
if(day >= 0)
for(d1 = 1970; day >= dysize(d1); d1++)
day -= dysize(d1);
else
for (d1 = 1970; day < 0; d1--)
day += dysize(d1-1);
xtime.year = d1-1900;
xtime.yday = d0 = day;
/*
* generate month
*/
if(dysize(d1) == 366)
dmsize[1] = 29;
for(d1 = 0; d0 >= dmsize[d1]; d1++)
d0 -= dmsize[d1];
dmsize[1] = 28;
xtime.mday = d0 + 1;
xtime.mon = d1;
jehanne_strcpy(xtime.zone, "GMT");
return &xtime;
}
char*
jehanne_asctime(Tm *t)
{
char *ncp;
static char cbuf[30];
jehanne_strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
cbuf[0] = *ncp++;
cbuf[1] = *ncp++;
cbuf[2] = *ncp;
ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
cbuf[4] = *ncp++;
cbuf[5] = *ncp++;
cbuf[6] = *ncp;
ct_numb(cbuf+8, t->mday);
ct_numb(cbuf+11, t->hour+100);
ct_numb(cbuf+14, t->min+100);
ct_numb(cbuf+17, t->sec+100);
ncp = t->zone;
cbuf[20] = *ncp++;
cbuf[21] = *ncp++;
cbuf[22] = *ncp;
if(t->year >= 100) {
cbuf[24] = '2';
cbuf[25] = '0';
}
ct_numb(cbuf+26, t->year+100);
return cbuf;
}
static
int
dysize(int y)
{
if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
return 366;
return 365;
}
static
void
ct_numb(char *cp, int n)
{
cp[0] = ' ';
if(n >= 10)
cp[0] = (n/10)%10 + '0';
cp[1] = n%10 + '0';
}
static
void
readtimezone(void)
{
char buf[TZSIZE*11+30], *p;
int i;
jehanne_memset(buf, 0, sizeof(buf));
i = sys_open("/env/timezone", OREAD);
if(i < 0)
goto error;
if(jehanne_read(i, buf, sizeof(buf)) >= sizeof(buf)){
sys_close(i);
goto error;
}
sys_close(i);
p = buf;
if(rd_name(&p, timezone.stname))
goto error;
if(rd_long(&p, &timezone.stdiff))
goto error;
if(rd_name(&p, timezone.dlname))
goto error;
if(rd_long(&p, &timezone.dldiff))
goto error;
for(i=0; i<TZSIZE; i++) {
if(rd_long(&p, &timezone.dlpairs[i]))
goto error;
if(timezone.dlpairs[i] == 0)
return;
}
error:
timezone.stdiff = 0;
jehanne_strcpy(timezone.stname, "GMT");
timezone.dlpairs[0] = 0;
}
static
int
rd_name(char **f, char *p)
{
int c, i;
for(;;) {
c = *(*f)++;
if(c != ' ' && c != '\n')
break;
}
for(i=0; i<3; i++) {
if(c == ' ' || c == '\n')
return 1;
*p++ = c;
c = *(*f)++;
}
if(c != ' ' && c != '\n')
return 1;
*p = 0;
return 0;
}
static
int
rd_long(char **f, int32_t *p)
{
int c, s;
int32_t l;
s = 0;
for(;;) {
c = *(*f)++;
if(c == '-') {
s++;
continue;
}
if(c != ' ' && c != '\n')
break;
}
if(c == 0) {
*p = 0;
return 0;
}
l = 0;
for(;;) {
if(c == ' ' || c == '\n')
break;
if(c < '0' || c > '9')
return 1;
l = l*10 + c-'0';
c = *(*f)++;
}
if(s)
l = -l;
*p = l;
return 0;
}

View File

@@ -0,0 +1,563 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
* See /doc/license/gpl-2.0.txt for details about the licensing.
*/
/*
* dial - connect to a service (parallel version)
*/
#include <u.h>
#include <libc.h>
typedef struct Conn Conn;
typedef struct Dest Dest;
typedef struct DS DS;
enum
{
Maxstring = 128,
Maxpath = 256,
Maxcsreply = 64*80, /* this is probably overly generous */
/*
* this should be a plausible slight overestimate for non-interactive
* use even if it's ridiculously long for interactive use.
*/
Maxconnms = 2*60*1000, /* 2 minutes */
};
struct DS {
/* dist string */
char buf[Maxstring];
char *netdir;
char *proto;
char *rem;
/* other args */
const char *local;
char *dir;
int *cfdp;
};
/*
* malloc these; they need to be writable by this proc & all children.
* the stack is private to each proc, and static allocation in the data
* segment would not permit concurrent dials within a multi-process program.
*/
struct Conn {
int pid;
int dead;
int dfd;
int cfd;
char dir[NETPATHLEN+1];
char err[ERRMAX];
};
struct Dest {
Conn *conn; /* allocated array */
Conn *connend;
int nkid;
int32_t oalarm;
int naddrs;
QLock winlck;
int winner; /* index into conn[] */
char *nextaddr;
char addrlist[Maxcsreply];
};
static int call(char*, char*, DS*, Dest*, Conn*);
static int csdial(DS*);
static void _dial_string_parse(const char*, DS*);
/*
* the dialstring is of the form '[/net/]proto!dest'
*/
static int
dialimpl(const char *dest, const char *local, char *dir, int *cfdp)
{
DS ds;
int rv;
char err[ERRMAX], alterr[ERRMAX];
ds.local = local;
ds.dir = dir;
ds.cfdp = cfdp;
_dial_string_parse(dest, &ds);
if(ds.netdir)
return csdial(&ds);
ds.netdir = "/net";
rv = csdial(&ds);
if(rv >= 0)
return rv;
err[0] = '\0';
sys_errstr(err, sizeof err);
if(jehanne_strstr(err, "refused") != 0){
jehanne_werrstr("%s", err);
return rv;
}
ds.netdir = "/net.alt";
rv = csdial(&ds);
if(rv >= 0)
return rv;
alterr[0] = 0;
sys_errstr(alterr, sizeof alterr);
if(jehanne_strstr(alterr, "translate") || jehanne_strstr(alterr, "does not exist"))
jehanne_werrstr("%s", err);
else
jehanne_werrstr("%s", alterr);
return rv;
}
/*
* the thread library can't cope with sys_rfork(RFMEM|RFPROC),
* so it must override this with a private version of dial.
*/
int (*_dial)(const char *, const char *, char *, int *) = dialimpl;
int
jehanne_dial(const char *dest, const char *local, char *dir, int *cfdp)
{
return (*_dial)(dest, local, dir, cfdp);
}
static int
connsalloc(Dest *dp, int addrs)
{
Conn *conn;
jehanne_free(dp->conn);
dp->connend = nil;
assert(addrs > 0);
dp->conn = jehanne_mallocz(addrs * sizeof *dp->conn, 1);
if(dp->conn == nil)
return -1;
dp->connend = dp->conn + addrs;
for(conn = dp->conn; conn < dp->connend; conn++)
conn->cfd = conn->dfd = -1;
return 0;
}
static void
freedest(Dest *dp)
{
int32_t oalarm;
if (dp == nil)
return;
oalarm = dp->oalarm;
jehanne_free(dp->conn);
jehanne_free(dp);
if (oalarm >= 0)
sys_alarm(oalarm);
}
static void
closeopenfd(int *fdp)
{
if (*fdp >= 0) {
sys_close(*fdp);
*fdp = -1;
}
}
static void
notedeath(Dest *dp, char *exitsts)
{
int i, n, pid;
char *fields[5]; /* pid + 3 times + error */
Conn *conn;
for (i = 0; i < nelem(fields); i++)
fields[i] = "";
n = jehanne_tokenize(exitsts, fields, nelem(fields));
if (n < 4)
return;
pid = jehanne_atoi(fields[0]);
if (pid <= 0)
return;
for (conn = dp->conn; conn < dp->connend; conn++)
if (conn->pid == pid && !conn->dead) { /* it's one we know? */
if (conn - dp->conn != dp->winner) {
closeopenfd(&conn->dfd);
closeopenfd(&conn->cfd);
}
jehanne_strncpy(conn->err, fields[4], sizeof conn->err - 1);
conn->err[sizeof conn->err - 1] = '\0';
conn->dead = 1;
return;
}
/* not a proc that we forked */
}
static int
outstandingprocs(Dest *dp)
{
Conn *conn;
for (conn = dp->conn; conn < dp->connend; conn++)
if (!conn->dead)
return 1;
return 0;
}
static int
reap(Dest *dp)
{
char exitsts[2*ERRMAX];
if (outstandingprocs(dp) && sys_await(exitsts, sizeof exitsts) >= 0) {
notedeath(dp, exitsts);
return 0;
}
return -1;
}
static int
fillinds(DS *ds, Dest *dp)
{
Conn *conn;
if (dp->winner < 0)
return -1;
conn = &dp->conn[dp->winner];
if (ds->cfdp)
*ds->cfdp = conn->cfd;
if (ds->dir) {
jehanne_strncpy(ds->dir, conn->dir, NETPATHLEN);
ds->dir[NETPATHLEN-1] = '\0';
}
return conn->dfd;
}
static int
connectwait(Dest *dp, char *besterr)
{
Conn *conn;
/* wait for a winner or all attempts to time out */
while (dp->winner < 0 && reap(dp) >= 0)
;
/* kill all of our still-live kids & reap them */
for (conn = dp->conn; conn < dp->connend; conn++)
if (!conn->dead)
jehanne_postnote(PNPROC, conn->pid, "alarm");
while (reap(dp) >= 0)
;
/* rummage about and report some error string */
for (conn = dp->conn; conn < dp->connend; conn++)
if (conn - dp->conn != dp->winner && conn->dead &&
conn->err[0]) {
jehanne_strncpy(besterr, conn->err, ERRMAX-1);
besterr[ERRMAX-1] = '\0';
break;
}
return dp->winner;
}
static int
parsecs(Dest *dp, char **clonep, char **destp)
{
char *dest, *p;
dest = jehanne_strchr(dp->nextaddr, ' ');
if(dest == nil) {
p = jehanne_strchr(dp->nextaddr, '\n');
if(p)
*p = '\0';
jehanne_werrstr("malformed clone cmd from cs `%s'", dp->nextaddr);
if(p)
*p = '\n';
return -1;
}
*dest++ = '\0';
p = jehanne_strchr(dest, '\n');
if(p == nil)
return -1;
*p++ = '\0';
*clonep = dp->nextaddr;
*destp = dest;
dp->nextaddr = p; /* advance to next line */
return 0;
}
static void
pickuperr(char *besterr, char *err)
{
err[0] = '\0';
sys_errstr(err, ERRMAX);
if(jehanne_strstr(err, "does not exist") == 0)
jehanne_strcpy(besterr, err);
}
static int
catcher(void *v, char *s)
{
return jehanne_strstr(s, "alarm") != nil;
}
/*
* try all addresses in parallel and take the first one that answers;
* this helps when systems have ip v4 and v6 addresses but are
* only reachable from here on one (or some) of them.
*/
static int
dialmulti(DS *ds, Dest *dp)
{
int rv, kid, kidme;
char *clone, *dest;
char besterr[ERRMAX];
dp->winner = -1;
dp->nkid = 0;
while(dp->winner < 0 && *dp->nextaddr != '\0' &&
parsecs(dp, &clone, &dest) >= 0) {
kidme = dp->nkid++; /* make private copy on stack */
kid = sys_rfork(RFPROC|RFMEM); /* spin off a call attempt */
if (kid < 0)
--dp->nkid;
else if (kid == 0) {
char err[ERRMAX];
/* only in kid, to avoid atnotify callbacks in parent */
jehanne_atnotify(catcher, 1);
*besterr = '\0';
rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
if(rv < 0)
pickuperr(besterr, err);
sys__exits(besterr); /* avoid atexit callbacks */
}
}
*besterr = '\0';
rv = connectwait(dp, besterr);
if(rv < 0)
jehanne_werrstr("%s", (*besterr? besterr: "unknown error"));
return rv;
}
static int
csdial(DS *ds)
{
int n, fd, rv, addrs, bleft;
char c;
char *addrp, *clone2, *dest;
char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
Dest *dp;
jehanne_werrstr("");
dp = jehanne_mallocz(sizeof *dp, 1);
if(dp == nil)
return -1;
dp->winner = -1;
dp->oalarm = sys_alarm(0);
if (connsalloc(dp, 1) < 0) { /* room for a single conn. */
freedest(dp);
return -1;
}
/*
* open connection server
*/
jehanne_snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
fd = sys_open(buf, ORDWR);
if(fd < 0){
/* no connection server, don't translate */
jehanne_snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
fillinds(ds, dp);
freedest(dp);
return rv;
}
/*
* ask connection server to translate
* e.g., net!cs.bell-labs.com!smtp
*/
jehanne_snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
if(jehanne_write(fd, buf, jehanne_strlen(buf)) < 0){
sys_close(fd);
freedest(dp);
return -1;
}
/*
* read all addresses from the connection server:
* /net/tcp/clone 135.104.9.78!25
* /net/tcp/clone 2620:0:dc0:1805::29!25
*
* assumes that we'll get one record per read.
*/
sys_seek(fd, 0, 0);
addrs = 0;
addrp = dp->nextaddr = dp->addrlist;
bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */
while(bleft > 0 && (n = jehanne_read(fd, addrp, bleft)) > 0) {
if (addrp[n-1] != '\n')
addrp[n++] = '\n';
addrs++;
addrp += n;
bleft -= n;
}
*addrp = '\0';
/*
* if we haven't read all of cs's output, assume the last line might
* have been truncated and ignore it. we really don't expect this
* to happen.
*/
if (addrs > 0 && bleft <= 0 && jehanne_read(fd, &c, 1) == 1)
addrs--;
sys_close(fd);
*besterr = 0;
rv = -1; /* pessimistic default */
dp->naddrs = addrs;
if (addrs == 0)
jehanne_werrstr("no address to dial");
else if (addrs == 1) {
/* common case: dial one address without forking */
if (parsecs(dp, &clone2, &dest) >= 0 &&
(rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
pickuperr(besterr, err);
jehanne_werrstr("%s", besterr);
}
} else if (connsalloc(dp, addrs) >= 0)
rv = dialmulti(ds, dp);
/* fill in results */
if (rv >= 0 && dp->winner >= 0)
rv = fillinds(ds, dp);
freedest(dp);
return rv;
}
static int
call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
{
int fd, cfd, n, calleralarm, oalarm;
char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
/* because cs is in a different name space, replace the mount point */
if(*clone == '/'){
p = jehanne_strchr(clone+1, '/');
if(p == nil)
p = clone;
else
p++;
} else
p = clone;
jehanne_snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
conn->pid = jehanne_getpid();
conn->cfd = cfd = sys_open(cname, ORDWR);
if(cfd < 0)
return -1;
/* get directory name */
n = jehanne_read(cfd, name, sizeof(name)-1);
if(n < 0){
closeopenfd(&conn->cfd);
return -1;
}
name[n] = 0;
for(p = name; *p == ' '; p++)
;
jehanne_snprint(name, sizeof(name), "%ld", jehanne_strtoul(p, 0, 0));
p = jehanne_strrchr(cname, '/');
*p = 0;
if(ds->dir)
jehanne_snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
jehanne_snprint(data, sizeof(data), "%s/%s/data", cname, name);
/* should be no alarm pending now; re-instate caller's alarm, if any */
calleralarm = dp->oalarm > 0;
if (calleralarm)
sys_alarm(dp->oalarm);
else if (dp->naddrs > 1) /* in a sub-process? */
sys_alarm(Maxconnms);
/* connect */
if(ds->local)
jehanne_snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
else
jehanne_snprint(name, sizeof(name), "connect %s", dest);
if(jehanne_write(cfd, name, jehanne_strlen(name)) < 0){
closeopenfd(&conn->cfd);
return -1;
}
oalarm = sys_alarm(0); /* don't let alarm interrupt critical section */
if (calleralarm)
dp->oalarm = oalarm; /* time has passed, so update user's */
/* open data connection */
conn->dfd = fd = sys_open(data, ORDWR);
if(fd < 0){
closeopenfd(&conn->cfd);
sys_alarm(dp->oalarm);
return -1;
}
if(ds->cfdp == nil)
closeopenfd(&conn->cfd);
n = conn - dp->conn;
if (dp->winner < 0) {
jehanne_qlock(&dp->winlck);
if (dp->winner < 0 && conn < dp->connend)
dp->winner = n;
jehanne_qunlock(&dp->winlck);
}
sys_alarm(calleralarm? dp->oalarm: 0);
return fd;
}
/*
* parse a dial string
*/
static void
_dial_string_parse(const char *str, DS *ds)
{
char *p, *p2;
jehanne_strncpy(ds->buf, str, Maxstring);
ds->buf[Maxstring-1] = 0;
p = jehanne_strchr(ds->buf, '!');
if(p == 0) {
ds->netdir = 0;
ds->proto = "net";
ds->rem = ds->buf;
} else {
if(*ds->buf != '/' && *ds->buf != '#'){
ds->netdir = 0;
ds->proto = ds->buf;
} else {
/* expecting /net.alt/tcp!foo or #I1/tcp!foo */
for(p2 = p; p2 > ds->buf && *p2 != '/'; p2--)
;
*p2++ = 0;
ds->netdir = ds->buf;
ds->proto = p2;
}
*p = 0;
ds->rem = p + 1;
}
}

View File

@@ -0,0 +1,46 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
enum
{
DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
};
Dir*
jehanne_dirfstat(int fd)
{
Dir *d;
uint8_t *buf;
int n, nd, i;
nd = DIRSIZE;
for(i=0; i<2; i++){ /* should work by the second try */
d = jehanne_malloc(sizeof(Dir) + BIT16SZ + nd);
if(d == nil)
return nil;
buf = (uint8_t*)&d[1];
n = sys_fstat(fd, buf, BIT16SZ+nd);
if(n < BIT16SZ){
jehanne_free(d);
return nil;
}
nd = GBIT16(buf); /* upper bound on size of Dir + strings */
if(nd <= n){
jehanne_convM2D(buf, n, d, (char*)&d[1]);
return d;
}
/* else sizeof(Dir)+BIT16SZ+nd is plenty */
jehanne_free(d);
}
return nil;
}

View File

@@ -0,0 +1,28 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
int
jehanne_dirfwstat(int fd, Dir *d)
{
uint8_t *buf;
int r;
r = jehanne_sizeD2M(d);
buf = jehanne_malloc(r);
if(buf == nil)
return -1;
jehanne_convD2M(d, buf, r);
r = sys_fwstat(fd, buf, r);
jehanne_free(buf);
return r;
}

View File

@@ -0,0 +1,57 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
static char *modes[] =
{
"---",
"--x",
"-w-",
"-wx",
"r--",
"r-x",
"rw-",
"rwx",
};
static void
rwx(int32_t m, char *s)
{
jehanne_strncpy(s, modes[m], 3);
}
int
jehanne_dirmodefmt(Fmt *f)
{
static char buf[16];
uint32_t m;
m = va_arg(f->args, uint32_t);
if(m & DMDIR)
buf[0]='d';
else if(m & DMAPPEND)
buf[0]='a';
else if(m & DMAUTH)
buf[0]='A';
else
buf[0]='-';
if(m & DMEXCL)
buf[1]='l';
else
buf[1]='-';
rwx((m>>6)&7, buf+2);
rwx((m>>3)&7, buf+5);
rwx((m>>0)&7, buf+8);
buf[11] = 0;
return jehanne_fmtstrcpy(f, buf);
}

View File

@@ -0,0 +1,106 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#define PORTABLE_SYSCALLS
#include <u.h>
#include <libc.h>
#include <9P2000.h>
static
int32_t
dirpackage(uint8_t *buf, int32_t ts, Dir **d)
{
char *s;
int32_t ss, i, n, nn, m;
*d = nil;
if(ts <= 0)
return 0;
/*
* first find number of all stats, check they look like stats, & size all associated strings
*/
ss = 0;
n = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16(&buf[i]);
if(statcheck(&buf[i], m) < 0)
break;
ss += m;
n++;
}
if(i != ts)
return -1;
*d = jehanne_malloc(n * sizeof(Dir) + ss);
if(*d == nil)
return -1;
/*
* then convert all buffers
*/
s = (char*)*d + n * sizeof(Dir);
nn = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16((uint8_t*)&buf[i]);
if(nn >= n || jehanne_convM2D(&buf[i], m, *d + nn, s) != m){
jehanne_free(*d);
*d = nil;
return -1;
}
nn++;
s += m;
}
return nn;
}
int32_t
jehanne_dirread(int fd, Dir **d)
{
uint8_t *buf;
int32_t ts;
buf = jehanne_malloc(DIRMAX);
if(buf == nil)
return -1;
ts = jehanne_read(fd, buf, DIRMAX);
if(ts >= 0)
ts = dirpackage(buf, ts, d);
jehanne_free(buf);
return ts;
}
int32_t
jehanne_dirreadall(int fd, Dir **d)
{
uint8_t *buf, *nbuf;
int32_t n, ts;
buf = nil;
ts = 0;
for(;;){
nbuf = jehanne_realloc(buf, ts+DIRMAX);
if(nbuf == nil){
jehanne_free(buf);
return -1;
}
buf = nbuf;
n = jehanne_read(fd, buf+ts, DIRMAX);
if(n <= 0)
break;
ts += n;
}
if(ts >= 0)
ts = dirpackage(buf, ts, d);
jehanne_free(buf);
if(ts == 0 && n < 0)
return -1;
return ts;
}

View File

@@ -0,0 +1,46 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
enum
{
DIRSIZE = STATFIXLEN + 16 * 4 /* enough for encoded stat buf + some reasonable strings */
};
Dir*
jehanne_dirstat(const char *name)
{
Dir *d;
uint8_t *buf;
int n, nd, i;
nd = DIRSIZE;
for(i=0; i<2; i++){ /* should work by the second try */
d = jehanne_malloc(sizeof(Dir) + BIT16SZ + nd);
if(d == nil)
return nil;
buf = (uint8_t*)&d[1];
n = jehanne_stat(name, buf, BIT16SZ+nd);
if(n < BIT16SZ){
jehanne_free(d);
return nil;
}
nd = GBIT16((uint8_t*)buf); /* upper bound on size of Dir + strings */
if(nd <= n){
jehanne_convM2D(buf, n, d, (char*)&d[1]);
return d;
}
/* else sizeof(Dir)+BIT16SZ+nd is plenty */
jehanne_free(d);
}
return nil;
}

View File

@@ -0,0 +1,28 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
int
jehanne_dirwstat(const char *name, Dir *d)
{
uint8_t *buf;
int r;
r = jehanne_sizeD2M(d);
buf = jehanne_malloc(r);
if(buf == nil)
return -1;
jehanne_convD2M(d, buf, r);
r = jehanne_wstat(name, buf, r);
jehanne_free(buf);
return r;
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_dup(int oldfd, int newfd)
{
FdPair in, out;
long f;
in.fd[0] = oldfd;
in.fd[1] = newfd;
f = sys_create("#d/new", -1, in.aslong);
if(f == -1)
return -1;
if(f >= 0){
/* this should never happen */
sys_close(f);
return -1;
}
out.aslong = ~f;
return out.fd[1];
}

View File

@@ -0,0 +1,17 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_fork(void)
{
return sys_rfork(RFPROC|RFFDG|RFREND);
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
char*
jehanne_getenv(const char *name)
{
int f;
int32_t l;
char path[127+5+1], *value;
assert(name != nil);
if(name[0]=='\0')
goto BadName;
if(jehanne_strcmp(name, ".")==0 || jehanne_strcmp(name, "..")==0)
goto BadName;
if(jehanne_strchr(name, '/')!=nil)
goto BadName;
jehanne_snprint(path, sizeof path, "/env/%s", name);
if(jehanne_strcmp(path+5, name) != 0)
goto BadName;
f = sys_open(path, OREAD);
if(f < 0){
/* try with #e, in case of a previous sys_rfork(RFCNAMEG)
*
* NOTE: /env is bound to #ec by default, so we
* cannot simply use always #e instead of /env. Also
* using #ec when the open in #e fails is both
* slow and not flexible enough.
*/
jehanne_snprint(path, sizeof path, "#e/%s", name);
f = sys_open(path, OREAD);
if(f < 0)
return nil;
}
l = sys_seek(f, 0, 2);
value = jehanne_malloc(l+1);
if(value == nil)
goto Done;
jehanne_setmalloctag(value, jehanne_getcallerpc());
sys_seek(f, 0, 0);
if(jehanne_read(f, value, l) >= 0)
value[l] = '\0';
Done:
sys_close(f);
return value;
BadName:
jehanne_werrstr("bad env name: '%s'", name);
return nil;
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
extern int32_t _mainpid; /* declared in $ARCH/argv0.c
* set by $ARCH/main9.S and $ARCH/syscall.c
*/
int32_t
jehanne_getmainpid(void)
{
/* getmainpid returns the pid of the process at the top of the
* stack, that is the process that started the main() function.
*
* it replace the old Plan9 _tos->pid
*/
return _mainpid;
}

View File

@@ -0,0 +1,142 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
static char *unknown = "???";
static void
getendpoint(char *dir, char *file, char **sysp, char **servp)
{
int fd, n;
char buf[128];
char *sys, *serv;
sys = serv = 0;
jehanne_snprint(buf, sizeof buf, "%s/%s", dir, file);
fd = sys_open(buf, OREAD);
if(fd >= 0){
n = jehanne_read(fd, buf, sizeof(buf)-1);
if(n>0){
buf[n-1] = 0;
serv = jehanne_strchr(buf, '!');
if(serv){
*serv++ = 0;
serv = jehanne_strdup(serv);
}
sys = jehanne_strdup(buf);
}
sys_close(fd);
}
if(serv == 0)
serv = unknown;
if(sys == 0)
sys = unknown;
*servp = serv;
*sysp = sys;
}
NetConnInfo*
jehanne_getnetconninfo(const char *dir, int fd)
{
NetConnInfo *nci;
char *cp;
Dir *d;
char spec[10];
char path[128];
char netname[128], *p;
/* get a directory address via fd */
if(dir == nil || *dir == 0){
if(sys_fd2path(fd, path, sizeof(path)) < 0)
return nil;
cp = jehanne_strrchr(path, '/');
if(cp == nil)
return nil;
*cp = 0;
dir = path;
}
nci = jehanne_mallocz(sizeof *nci, 1);
if(nci == nil)
return nil;
/* copy connection directory */
nci->dir = jehanne_strdup(dir);
if(nci->dir == nil)
goto err;
/* get netroot */
nci->root = jehanne_strdup(dir);
if(nci->root == nil)
goto err;
cp = jehanne_strchr(nci->root+1, '/');
if(cp == nil)
goto err;
*cp = 0;
/* figure out bind spec */
d = jehanne_dirstat(nci->dir);
if(d != nil){
jehanne_sprint(spec, "#%C%d", d->type, d->dev);
nci->spec = jehanne_strdup(spec);
}
if(nci->spec == nil)
nci->spec = unknown;
jehanne_free(d);
/* get the two end points */
getendpoint(nci->dir, "local", &nci->lsys, &nci->lserv);
if(nci->lsys == nil || nci->lserv == nil)
goto err;
getendpoint(nci->dir, "remote", &nci->rsys, &nci->rserv);
if(nci->rsys == nil || nci->rserv == nil)
goto err;
jehanne_strecpy(netname, netname+sizeof netname, nci->dir);
if((p = jehanne_strrchr(netname, '/')) != nil)
*p = 0;
if(jehanne_strncmp(netname, "/net/", 5) == 0)
jehanne_memmove(netname, netname+5, jehanne_strlen(netname+5)+1);
nci->laddr = jehanne_smprint("%s!%s!%s", netname, nci->lsys, nci->lserv);
nci->raddr = jehanne_smprint("%s!%s!%s", netname, nci->rsys, nci->rserv);
if(nci->laddr == nil || nci->raddr == nil)
goto err;
return nci;
err:
jehanne_freenetconninfo(nci);
return nil;
}
static void
xfree(char *x)
{
if(x == nil || x == unknown)
return;
jehanne_free(x);
}
void
jehanne_freenetconninfo(NetConnInfo *nci)
{
if(nci == nil)
return;
xfree(nci->root);
xfree(nci->dir);
xfree(nci->spec);
xfree(nci->lsys);
xfree(nci->lserv);
xfree(nci->rsys);
xfree(nci->rserv);
xfree(nci->laddr);
xfree(nci->raddr);
jehanne_free(nci);
}

View File

@@ -0,0 +1,28 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_getpid(void)
{
int pid;
pid = (int)sys_remove("#0/pid");
return pid;
}

View File

@@ -0,0 +1,28 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_getppid(void)
{
int pid;
pid = (int)sys_remove("#0/ppid");
return pid;
}

View File

@@ -0,0 +1,45 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
/* Fills buf with the current workking directory.
*
* Returns
* - 0 on error
* - the negated length of the working directory path if nbuf is too small
* - the (positive) length of the working directory on success
*/
long
jehanne_getwd(char *buf, int nbuf)
{
long n;
int fd;
fd = sys_open("#0/wdir", OREAD);
if(fd < 0)
return 0;
n = jehanne_read(fd, nil, -1);
if(n == ~0) /* an error occurred */
return 0;
if(nbuf >= ~n)
n = jehanne_read(fd, buf, nbuf);
sys_close(fd);
return n;
}

View File

@@ -0,0 +1,36 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
/*
* Format:
3 r M 4 (0000000000457def 11 00) 8192 512 /arch/rc/lib/rcmain
*/
int
jehanne_iounit(int fd)
{
int i, cfd;
char buf[128], *args[10];
jehanne_snprint(buf, sizeof buf, "#d/%dctl", fd);
cfd = sys_open(buf, OREAD);
if(cfd < 0)
return 0;
i = jehanne_read(cfd, buf, sizeof buf-1);
sys_close(cfd);
if(i <= 0)
return 0;
buf[i] = '\0';
if(jehanne_tokenize(buf, args, nelem(args)) != nelem(args))
return 0;
return jehanne_atoi(args[7]);
}

View File

@@ -0,0 +1,30 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#define PORTABLE_SYSCALLS
#include <u.h>
#include <libc.h>
unsigned long
jehanne_nsec(void)
{
long r;
r = sys_remove("/dev/time");
if(r == -1)
r = sys_remove("#c/time");
return r;
}

View File

@@ -0,0 +1,18 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
void
jehanne_nulldir(Dir *d)
{
jehanne_memset(d, ~0, sizeof(Dir));
d->name = d->uid = d->gid = d->muid = "";
}

View File

@@ -0,0 +1,69 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
/* ocreate works like the Plan 9 create(2) syscall, but with different races.
* In Plan 9 there is a race due to the different behaviour between the
* create syscall and the Tcreate message in 9P2000 when the file already exists:
* see https://github.com/brho/plan9/blob/master/sys/src/9/port/chan.c#L1564-L1603
* for details.
*
* In Jehanne the create syscall fails on existing files just like the Tcreate message.
* However the Plan 9/UNIX semantic is often useful, thus ocreate mimic it in userspace.
* As wisely noted by Charles Forsyth, such implementation introduce a different race
* due to the multiple evaluations of the path: on concurrent namespace changes, the
* different syscalls here could be handled by different devices/fileservers.
* However, given the user is responsible of such namespace changes, we prefer this race
* to the original Plan 9 one.
*
* For more info see http://marc.info/?t=146412533100003&r=1&w=2
*/
int
jehanne_ocreate(const char *path, unsigned int omode, unsigned int perm)
{
int fd;
Dir *s;
fd = sys_open(path, omode|OTRUNC);
if(fd < 0){
fd = sys_create(path, omode, perm);
if(fd < 0){
fd = sys_open(path, omode|OTRUNC);
if(fd < 0)
goto Done;
} else {
goto Done;
}
}
s = jehanne_dirfstat(fd);
if(s == nil){
sys_close(fd);
return -1;
}
if(s->mode != perm){
s->mode = perm;
jehanne_dirfwstat(fd, s); /* we ignore the return value, the device/server is allowed to ignore us */
}
jehanne_free(s);
Done:
return fd;
}

View File

@@ -0,0 +1,41 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
#include <envvars.h>
int
jehanne_pexec(const char *f, char *args[])
{
char *epath, *path, *entries[33];
int n, i;
if(f[0] == '/' || (epath = jehanne_getenv(ENV_PATH)) == nil)
return sys_exec(f, (const char**)args);
n = jehanne_getfields(epath, entries, nelem(entries), 1, ":");
for(i = 0; i < n; ++i){
path = jehanne_smprint("%s/%s", entries[i], f);
sys_exec(path, (const char**)args);
jehanne_free(path);
}
jehanne_free(epath);
return -1;
}

View File

@@ -0,0 +1,31 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_pipe(int pipes[2])
{
FdPair pset;
pset.aslong = sys_remove("#0/pipes");
if(pset.aslong == -1)
return -1;
pipes[0] = pset.fd[0];
pipes[1] = pset.fd[1];
return 0;
}

View File

@@ -0,0 +1,82 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
static int
openprocnotes(int pid)
{
char file[128];
int32_t f;
jehanne_sprint(file, "#p/%d/note", pid);
f = sys_open(file, OWRITE);
if(f < 0){
jehanne_sprint(file, "/proc/%d/note", pid);
f = sys_open(file, OWRITE);
}
if(f < 0)
jehanne_werrstr("postnote: cannot open neither #p/%d/note nor /proc/%d/note", pid, pid);
return f;
}
static int
opengroupnotes(int pid)
{
char file[128];
int32_t f;
jehanne_sprint(file, "#p/%d/notepg", pid);
f = sys_open(file, OWRITE);
if(f < 0){
jehanne_sprint(file, "/proc/%d/notepg", pid);
f = sys_open(file, OWRITE);
}
if(f < 0)
jehanne_werrstr("postnote: cannot open neither #p/%d/notepg nor /proc/%d/notepg", pid, pid);
return f;
}
int
jehanne_postnote(int group, int pid, const char *note)
{
int f, r;
switch(group) {
case PNPROC:
f = openprocnotes(pid);
break;
case PNGROUP:
f = opengroupnotes(pid);
break;
default:
jehanne_werrstr("postnote: invalid group flag %d", group);
return -1;
}
if(f < 0)
return f;
r = jehanne_strlen(note);
if(jehanne_write(f, note, r) != r) {
sys_close(f);
return -1;
}
sys_close(f);
return 0;
}

View File

@@ -0,0 +1,22 @@
#include <u.h>
#include <libc.h>
static Lock privlock;
extern void **_privates;
extern int _nprivates;
void **
jehanne_privalloc(void)
{
void **p;
jehanne_lock(&privlock);
if(_nprivates > 0)
p = &_privates[--_nprivates];
else
p = nil;
jehanne_unlock(&privlock);
return p;
}

View File

@@ -0,0 +1,53 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
/*
* Since the SSL device uses decimal file descriptors to name channels,
* it is impossible for a user-level file server to stand in for the kernel device.
* Thus we hard-code #D rather than use /net/ssl.
*/
int
jehanne_pushssl(int fd, const char *alg, const char *secin, const char *secout, int *cfd)
{
char buf[8];
char dname[64];
int n, data, ctl;
ctl = sys_open("#D/ssl/clone", ORDWR);
if(ctl < 0)
return -1;
n = jehanne_read(ctl, buf, sizeof(buf)-1);
if(n < 0)
goto error;
buf[n] = 0;
jehanne_sprint(dname, "#D/ssl/%s/data", buf);
data = sys_open(dname, ORDWR);
if(data < 0)
goto error;
if(jehanne_fprint(ctl, "fd %d", fd) < 0 ||
jehanne_fprint(ctl, "secretin %s", secin) < 0 ||
jehanne_fprint(ctl, "secretout %s", secout) < 0 ||
jehanne_fprint(ctl, "alg %s", alg) < 0){
sys_close(data);
goto error;
}
sys_close(fd);
if(cfd != 0)
*cfd = ctl;
else
sys_close(ctl);
return data;
error:
sys_close(ctl);
return -1;
}

View File

@@ -0,0 +1,62 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_putenv(const char *name, const char *value)
{
int f;
int32_t l;
char path[127+5+1];
assert(name != nil);
assert(value != nil);
if(name[0]=='\0')
goto BadName;
if(jehanne_strcmp(name, ".")==0 || jehanne_strcmp(name, "..")==0)
goto BadName;
if(jehanne_strchr(name, '/')!=nil)
goto BadName;
jehanne_snprint(path, sizeof path, "/env/%s", name);
if(jehanne_strcmp(path+5, name) != 0)
goto BadName;
f = jehanne_ocreate(path, OWRITE, 0664);
if(f < 0){
/* try with #e, in case of a previous sys_rfork(RFCNAMEG)
*/
jehanne_snprint(path, sizeof path, "#e/%s", name);
f = jehanne_ocreate(path, OWRITE, 0664);
if(f < 0)
return -1;
}
l = jehanne_strlen(value);
if(l > 0 && jehanne_write(f, value, l) != l){
sys_close(f);
return -1;
}
sys_close(f);
return 0;
BadName:
jehanne_werrstr("bad env name: '%s'", name);
return -1;
}

View File

@@ -0,0 +1,823 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
static struct {
QLp *p;
QLp x[1024];
} ql = {
ql.x
};
/* The possible state transitions of a QLp are
*
* Free -> Queuing -> Timedout|Done -> Free
* Free -> QueuingR -> Timedout|Done -> Free
* Free -> QueuingW -> Timedout|Done -> Free
* Free -> Sleeping -> Timedout -> Free
* Free -> Sleeping -> Queuing -> Done -> Free
*
* QLp starts as Free and move to one of the custom states on getqlp.
* Timedout is optional and alternative to Done.
* Timedout can only occur if the QLp was allocated by a variant that
* support timeouts (qlockt, rlockt, wlockt and rsleept).
* Done is reached just before wakeup if and only if the current state
* is the expected custom one (all transitions use CAS).
*
* All QLp need to reach the Free state to be released.
*
* All transitions are protected by a lock, except
* - the ones that lead to Timedout, so that if a QLp is not in
* the expected state, it's Timedout.
* - the ones from Done to Free since Done is
* a terminal state: Freeing is just book keeping.
*
* NOTE: The rsleep/rwakeup transitions mean that a you should never
* use a qlockt a Qlock that is going to be used with a rsleept.
*/
enum
{
Free,
Queuing,
QueuingR,
QueuingW,
Sleeping,
Timedout,
Done,
};
#if 0
typedef struct RendezvousLog
{
void *tag;
long pid;
long start;
long end;
void *addr;
void *caller;
void *r;
} RendezvousLog;
RendezvousLog logs[1024];
int logidx;
static void*
debugrendezvous(void *tag, void *val)
{
static void** pidp;
int i;
if(pidp == nil)
pidp = jehanne_privalloc();
if(*pidp == 0)
*pidp = (void*)(long)jehanne_getpid();
i = jehanne_ainc(&logidx) - 1;
logs[i].tag = tag;
logs[i].pid = (long)*pidp;
logs[i].start = jehanne_nsec();
logs[i].addr = __builtin_return_address(0);
logs[i].caller = __builtin_return_address(1);
logs[i].r = (void*)0xffabcdefffabcdef;
logs[i].r = sys_rendezvous(tag, (void*)logs[i].addr);
logs[i].end = jehanne_nsec();
return logs[i].r;
}
void
printdebugrendezvouslogs(void)
{
int i;
for(i = 0; i < logidx; ++i)
jehanne_print("[%d] %#p @ %#p sys_rendezvous(%#p, %#p) -> %#p @ %#p\n", logs[i].pid, logs[i].caller, logs[i].start, logs[i].tag, logs[i].addr, logs[i].r, logs[i].end);
}
static void* (*_rendezvousp)(void*, void*) = debugrendezvous;
#else
static void*
__rendezvous(void* tag, void* val)
{
return sys_rendezvous(tag, val);
}
static void* (*_rendezvousp)(void*, void*) = __rendezvous;
#endif
# define RENDEZVOUS(...) (*_rendezvousp)(__VA_ARGS__)
//# define RENDEZVOUS(...) sys_rendezvous(__VA_ARGS__)
//# define RENDEZVOUS(tag, val) __rendezvous(tag, __builtin_return_address(0))
/* this gets called by the thread library ONLY to get us to use its rendezvous */
void
jehanne__qlockinit(void* (*r)(void*, void*))
{
_rendezvousp = r;
}
/* find a free shared memory location to queue ourselves in */
static QLp*
getqlp(uint8_t use)
{
QLp *p, *op;
op = ql.p;
for(p = op+1; ; p++){
if(p == &ql.x[nelem(ql.x)])
p = ql.x;
if(p == op)
abort();
if(cas(&p->state, Free, use)){
ql.p = p;
p->next = nil;
break;
}
}
return p;
}
void
jehanne_qlock(QLock *q)
{
QLp *p, *mp;
jehanne_lock(&q->lock);
if(!q->locked){
q->locked = 1;
jehanne_unlock(&q->lock);
return;
}
/* chain into waiting list */
mp = getqlp(Queuing);
p = q->tail;
if(p == nil)
q->head = mp;
else
p->next = mp;
q->tail = mp;
jehanne_unlock(&q->lock);
/* wait */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
;
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
}
void
jehanne_qunlock(QLock *q)
{
QLp *p, *tmp;
jehanne_lock(&q->lock);
if (q->locked == 0)
jehanne_fprint(2, "qunlock called with qlock not held, from %#p\n",
jehanne_getcallerpc());
p = q->head;
while(p != nil && !cas(&p->state, Queuing, Done)){
/* the lock in p timed out */
if(p->state != Timedout){
jehanne_print("qunlock mp %#p: state %d (should be Timedout) pc %#p\n", p, p->state, (uintptr_t)__builtin_return_address(0));
abort();
}
tmp = p->next;
p->state = Free;
p = tmp;
}
if(p != nil){
/* wakeup head waiting process */
if(p->state != Done){
jehanne_print("qunlock: p %#p p->state %d\n", p, p->state);
abort();
}
q->head = p->next;
if(q->head == nil)
q->tail = nil;
jehanne_unlock(&q->lock);
while(RENDEZVOUS(p, (void*)0x12345) == (void*)~0)
;
return;
} else {
/* all pending locks timedout */
q->head = nil;
q->tail = nil;
}
q->locked = 0;
jehanne_unlock(&q->lock);
}
int
jehanne_qlockt(QLock *q, uint32_t ms)
{
QLp *p, *mp;
int64_t wkup;
if(!jehanne_lockt(&q->lock, ms))
return 0;
if(!q->locked){
q->locked = 1;
jehanne_unlock(&q->lock);
return 1;
}
/* chain into waiting list */
mp = getqlp(Queuing);
p = q->tail;
if(p == nil)
q->head = mp;
else
p->next = mp;
q->tail = mp;
jehanne_unlock(&q->lock);
/* set up awake to interrupt rendezvous */
wkup = sys_awake(ms);
/* wait */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
if (jehanne_awakened(wkup)){
/* interrupted by awake */
if(cas(&mp->state, Queuing, Timedout))
/* if we can atomically mark the QLp
* the next qunlock will release it...
*/
return 0;
/* ... otherwise we are going to take the lock
* on the next rendezvous from qunlock
*/
assert(mp->state == Done);
}
jehanne_forgivewkp(wkup);
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
return 1;
}
int
jehanne_canqlock(QLock *q)
{
if(!jehanne_canlock(&q->lock))
return 0;
if(!q->locked){
q->locked = 1;
jehanne_unlock(&q->lock);
return 1;
}
jehanne_unlock(&q->lock);
return 0;
}
void
jehanne_rlock(RWLock *q)
{
QLp *p, *mp;
jehanne_lock(&q->lock);
if(q->writer == 0 && q->head == nil){
/* no writer, go for it */
q->_readers++;
jehanne_unlock(&q->lock);
return;
}
mp = getqlp(QueuingR);
p = q->tail;
if(p == 0)
q->head = mp;
else
p->next = mp;
q->tail = mp;
mp->next = nil;
jehanne_unlock(&q->lock);
/* wait in kernel */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
;
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
}
int
jehanne_rlockt(RWLock *q, uint32_t ms)
{
QLp *p, *mp;
int64_t wkup;
if(!jehanne_lockt(&q->lock, ms))
return 0;
if(q->writer == 0 && q->head == nil){
/* no writer, go for it */
q->_readers++;
jehanne_unlock(&q->lock);
return 1;
}
/* chain into waiting list */
mp = getqlp(QueuingR);
p = q->tail;
if(p == 0)
q->head = mp;
else
p->next = mp;
q->tail = mp;
mp->next = nil;
jehanne_unlock(&q->lock);
/* set up awake to interrupt rendezvous */
wkup = sys_awake(ms);
/* wait in kernel */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
if (jehanne_awakened(wkup)){
/* interrupted by awake */
if(cas(&mp->state, QueuingR, Timedout))
/* if we can atomically mark the QLp
* a future wunlock will release it...
*/
return 0;
/* ... otherwise we are going to take the lock
* on the next rendezvous from wunlock
*/
assert(mp->state == Done);
}
jehanne_forgivewkp(wkup);
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
return 1;
}
int
jehanne_canrlock(RWLock *q)
{
jehanne_lock(&q->lock);
if (q->writer == 0 && q->head == nil) {
/* no writer; go for it */
q->_readers++;
jehanne_unlock(&q->lock);
return 1;
}
jehanne_unlock(&q->lock);
return 0;
}
void
jehanne_runlock(RWLock *q)
{
QLp *p, *tmp;
jehanne_lock(&q->lock);
if(q->_readers <= 0)
abort();
p = q->head;
if(--(q->_readers) > 0 || p == nil){
runlockWithoutWriters:
jehanne_unlock(&q->lock);
return;
}
/* start waiting writer */
while(p != nil && !cas(&p->state, QueuingW, Done)){
/* the lock in p timed out
*
* Note that p cannot have reached Done or Free already
* since we hold q->lock, and the only transactions that
* do not require this lock are timeout ones.
*/
if(p->state != Timedout)
abort();
tmp = p->next;
p->state = Free;
p = tmp;
}
if(p == nil)
goto runlockWithoutWriters;
q->head = p->next;
if(q->head == 0)
q->tail = 0;
q->writer = 1;
jehanne_unlock(&q->lock);
/* wakeup waiter */
while(RENDEZVOUS(p, 0) == (void*)~0)
;
}
void
jehanne_wlock(RWLock *q)
{
QLp *p, *mp;
jehanne_lock(&q->lock);
if(q->_readers == 0 && q->writer == 0){
/* noone waiting, go for it */
q->writer = 1;
jehanne_unlock(&q->lock);
return;
}
/* chain into waiting list */
p = q->tail;
mp = getqlp(QueuingW);
if(p == nil)
q->head = mp;
else
p->next = mp;
q->tail = mp;
mp->next = nil;
jehanne_unlock(&q->lock);
/* wait in kernel */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
;
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
}
int
jehanne_wlockt(RWLock *q, uint32_t ms)
{
QLp *p, *mp;
int64_t wkup;
if(!jehanne_lockt(&q->lock, ms))
return 0;
if(q->_readers == 0 && q->writer == 0){
/* noone waiting, go for it */
q->writer = 1;
jehanne_unlock(&q->lock);
return 1;
}
/* chain into waiting list */
p = q->tail;
mp = getqlp(QueuingW);
if(p == nil)
q->head = mp;
else
p->next = mp;
q->tail = mp;
mp->next = nil;
jehanne_unlock(&q->lock);
/* set up awake to interrupt rendezvous */
wkup = sys_awake(ms);
/* wait in kernel */
while(RENDEZVOUS(mp, (void*)1) == (void*)~0)
if (jehanne_awakened(wkup)){
/* interrupted by awake */
if(cas(&mp->state, QueuingW, Timedout))
/* if we can atomically mark the QLp
* a future runlock/wunlock will release it...
*/
return 0;
/* ... otherwise we are going to take the lock
* on the next rendezvous from runlock/wunlock
*/
assert(mp->state == Done);
}
jehanne_forgivewkp(wkup);
if(!cas(&mp->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", mp, mp->state, __builtin_return_address(0));
abort();
}
return 1;
}
int
jehanne_canwlock(RWLock *q)
{
jehanne_lock(&q->lock);
if (q->_readers == 0 && q->writer == 0) {
/* no one waiting; go for it */
q->writer = 1;
jehanne_unlock(&q->lock);
return 1;
}
jehanne_unlock(&q->lock);
return 0;
}
void
jehanne_wunlock(RWLock *q)
{
QLp *p, *tmp;
jehanne_lock(&q->lock);
if(q->writer == 0)
abort();
p = q->head;
if(p == nil){
q->writer = 0;
jehanne_unlock(&q->lock);
return;
}
while(p != nil){
wakeupPendingWriter: /* when we jump here, we know p is not nil */
switch(casv(&p->state, QueuingW, Done)){
case QueuingW:
/* start waiting writer */
q->head = p->next;
if(q->head == nil)
q->tail = nil;
jehanne_unlock(&q->lock);
while(RENDEZVOUS(p, 0) == (void*)~0)
;
return;
case Timedout:
/* R and W have the same fate, once Timedout */
tmp = p->next;
p->state = Free;
p = tmp;
break;
case QueuingR:
/* wakeup pending readers */
goto wakeupPendingReaders;
default:
jehanne_print("wunlock: %#p has state %d instead of QueuingW\n", p, p->state);
abort();
break;
}
}
/* wake waiting readers */
while(p != nil){
wakeupPendingReaders: /* when we jump here, we know p is not nil */
switch(casv(&p->state, QueuingR, Done)){
case QueuingR:
q->_readers++;
tmp = p->next;
jehanne_unlock(&q->lock);
while(RENDEZVOUS(p, 0) == (void*)~0)
;
/* after the rendezvous, p->state will be set to Free
* from the reader we are going to wakeup, thus p could
* be reused before we are scheduled again: we need tmp
* to keep track of the next QLp to test
*/
jehanne_lock(&q->lock);
p = tmp;
break;
case Timedout:
/* R and W have the same fate, once Timedout */
tmp = p->next;
p->state = Free;
p = tmp;
break;
case QueuingW:
if(q->_readers > 0){
goto allPendingReadersStarted;
} else {
/* all readers timedout: wakeup the pending writer */
goto wakeupPendingWriter;
}
default:
jehanne_print("wunlock: %#p has state %d instead of QueuingR\n", p, p->state);
abort();
break;
}
}
allPendingReadersStarted:
q->head = p;
if(q->head == nil)
q->tail = nil;
q->writer = 0;
jehanne_unlock(&q->lock);
}
void
jehanne_rsleep(Rendez *r)
{
QLp *t, *me, *tmp;
if(!r->l)
abort();
jehanne_lock(&r->l->lock);
/* we should hold the qlock */
if(!r->l->locked)
abort();
/* add ourselves to the wait list */
me = getqlp(Sleeping);
if(r->head == nil)
r->head = me;
else
r->tail->next = me;
me->next = nil;
r->tail = me;
/* pass the qlock to the next guy */
t = r->l->head;
while(t != nil && !cas(&t->state, Queuing, Done)){
/* the lock in t timed out */
if(t->state != Timedout){
jehanne_print("rsleep mp %#p: state %d (should be Timedout) pc %#p\n", t, t->state, __builtin_return_address(0));
abort();
}
tmp = t->next;
t->state = Free;
t = tmp;
}
if(t != nil){
r->l->head = t->next;
if(r->l->head == nil)
r->l->tail = nil;
jehanne_unlock(&r->l->lock);
while(RENDEZVOUS(t, (void*)0x12345) == (void*)~0)
;
}else{
r->l->head = nil;
r->l->tail = nil;
r->l->locked = 0;
jehanne_unlock(&r->l->lock);
}
/* wait for a wakeup */
while(RENDEZVOUS(me, (void*)1) == (void*)~0)
;
if(!cas(&me->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", me, me->state, __builtin_return_address(0));
abort();
}
}
int
jehanne_rsleept(Rendez *r, uint32_t ms)
{
QLp *t, *me, *tmp;
int64_t wkup;
if(!r->l)
abort();
if(!jehanne_lockt(&r->l->lock, ms))
return 0;
/* we should hold the qlock */
if(!r->l->locked)
abort();
/* add ourselves to the wait list */
me = getqlp(Sleeping);
if(r->head == nil)
r->head = me;
else
r->tail->next = me;
me->next = nil;
r->tail = me;
/* pass the qlock to the next guy */
t = r->l->head;
while(t != nil && !cas(&t->state, Queuing, Done)){
/* the lock in t timed out */
if(t->state != Timedout){
jehanne_print("rsleept mp %#p: state %d (should be Timedout) pc %#p\n", t, t->state, __builtin_return_address(0));
abort();
}
tmp = t->next;
t->state = Free;
t = tmp;
}
if(t != nil){
r->l->head = t->next;
if(r->l->head == nil)
r->l->tail = nil;
jehanne_unlock(&r->l->lock);
while(RENDEZVOUS(t, (void*)0x12345) == (void*)~0)
;
}else{
r->l->head = nil;
r->l->tail = nil;
r->l->locked = 0;
jehanne_unlock(&r->l->lock);
}
/* set up awake to interrupt rendezvous */
wkup = sys_awake(ms);
/* wait for a rwakeup (or a timeout) */
while(RENDEZVOUS(me, (void*)1) == (void*)~0)
if (jehanne_awakened(wkup)){
if(cas(&me->state, Sleeping, Timedout)){
/* if we can atomically mark the QLp
* a future rwakeup will release it...
*/
jehanne_qlock(r->l);
return 0;
}
/* ... otherwise we are going to take the lock
* on the next rendezvous from rwakeup
*/
assert(me->state == Done);
}
jehanne_forgivewkp(wkup);
if(!cas(&me->state, Done, Free)){
jehanne_print("mp %#p: state %d pc %#p\n", me, me->state, __builtin_return_address(0));
abort();
}
return 1;
}
int
jehanne_rwakeup(Rendez *r)
{
QLp *t, *tmp;
/*
* take off wait and put on front of queue
* put on front so guys that have been waiting will not get starved
*/
if(!r->l)
abort();
jehanne_lock(&r->l->lock);
if(!r->l->locked)
abort();
t = r->head;
if(t != nil && t->state == Free)
abort();
while(t != nil && !cas(&t->state, Sleeping, Queuing)){
if(t->state != Timedout){
jehanne_print("rwakeup mp %#p: state %d (should be Timedout) pc %#p\n", t, t->state, __builtin_return_address(0));
abort();
}
tmp = t->next;
t->state = Free;
t = tmp;
}
if(t == nil){
r->head = nil;
r->tail = nil;
jehanne_unlock(&r->l->lock);
return 0;
}
r->head = t->next;
if(r->head == nil)
r->tail = nil;
t->next = r->l->head;
r->l->head = t;
if(r->l->tail == nil)
r->l->tail = t;
jehanne_unlock(&r->l->lock);
return 1;
}
int
jehanne_rwakeupall(Rendez *r)
{
int i;
for(i=0; jehanne_rwakeup(r); i++)
;
return i;
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2020 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
long
jehanne_read(int fd, void* buf, int nbytes)
{
return sys_pread(fd, buf, nbytes, ~0LL);
}

View File

@@ -0,0 +1,22 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
void
jehanne_rerrstr(char *buf, uint32_t nbuf)
{
char tmp[ERRMAX];
tmp[0] = 0;
sys_errstr(tmp, sizeof tmp);
jehanne_utfecpy(buf, buf+nbuf, tmp);
sys_errstr(tmp, sizeof tmp);
}

View File

@@ -0,0 +1,68 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
void*
jehanne_segattach(int attr, const char *class, void *va, unsigned long len)
{
int fd;
long tmp;
char msg[256];
fd = sys_open("#0/segments", OWRITE);
if(fd < 0)
return (void*)-1;
tmp = jehanne_snprint(msg, sizeof(msg), "attach 0x%ux %#p %ulld %s", attr, va, len, class);
tmp = jehanne_write(fd, msg, tmp);
sys_close(fd);
return (void*)tmp;
}
int
jehanne_segdetach(void *addr)
{
int fd, tmp;
char msg[256];
fd = sys_open("#0/segments", OWRITE);
if(fd < 0)
return -1;
tmp = jehanne_snprint(msg, sizeof(msg), "detach %#p", addr);
tmp = jehanne_write(fd, msg, tmp);
sys_close(fd);
return tmp;
}
int
jehanne_segfree(void *addr, unsigned long len)
{
int fd, tmp;
char msg[256];
fd = sys_open("#0/segments", OWRITE);
if(fd < 0)
return -1;
tmp = jehanne_snprint(msg, sizeof(msg), "free %#p %ulld", addr, len);
tmp = jehanne_write(fd, msg, tmp);
sys_close(fd);
return tmp;
}

View File

@@ -0,0 +1,60 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#define PORTABLE_SYSCALLS
#include <u.h>
#include <libc.h>
extern char end[];
static char *last_brk = { end };
#define ROUND_BRK(b) (((uintptr_t)b + 7) & ~7)
static uintptr_t
setbrk(uintptr_t p)
{
long b;
/* assert: devself is still working */
assert((b = sys_create("#0/brk/set", -1, p)) < 0);
if(b == -1)
return ~0; // an error occurred
return (uintptr_t)~b;
}
int
jehanne_brk(void *p)
{
uintptr_t new_brk;
new_brk = ROUND_BRK(p);
if(setbrk(new_brk) == ~0)
return -1;
last_brk = (char*)new_brk;
return 0;
}
void*
jehanne_segbrk(uint32_t increment)
{
uintptr_t new_brk;
new_brk = ROUND_BRK(last_brk);
if(setbrk(new_brk+increment) == ~0)
return (void*)-1;
last_brk = (char*)new_brk + increment;
return (void*)new_brk;
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
void
jehanne_setnetmtpt(char *net, int n, const char *x)
{
if(x == nil)
x = "/net";
if(*x == '/'){
jehanne_strncpy(net, x, n);
net[n-1] = 0;
} else {
jehanne_snprint(net, n, "/net%s", x);
}
}

View File

@@ -0,0 +1,32 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
void
jehanne_sleep(unsigned int millisecs)
{
long wakeup;
wakeup = sys_awake(millisecs); // give up the processor, in any case
if(millisecs > 0)
while(sys_rendezvous((void*)~0, (void*)1) == (void*)~0)
if(jehanne_awakened(wakeup))
return;
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_stat(const char* name, uint8_t* edir, int nedir)
{
int fd, statsz;
fd = sys_open(name, OSTAT);
if(fd < 0){
jehanne_werrstr("stat: %r");
return fd;
}
statsz = sys_fstat(fd, edir, nedir);
sys_close(fd);
return statsz;
}
int
jehanne_wstat(const char* name, uint8_t* edir, int nedir)
{
int fd, statsz;
fd = sys_open(name, OSTAT);
if(fd < 0){
jehanne_werrstr("wstat: %r");
return fd;
}
statsz = sys_fwstat(fd, edir, nedir);
sys_close(fd);
return statsz;
}

View File

@@ -0,0 +1,38 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
static void
_sysfatalimpl(const char *fmt, va_list arg)
{
char buf[1024];
jehanne_vseprint(buf, buf+sizeof(buf), fmt, arg);
if(argv0)
jehanne_fprint(2, "%s: %s\n", argv0, buf);
else
jehanne_fprint(2, "%s\n", buf);
jehanne_exits(buf);
}
void (*_sysfatal)(const char *fmt, va_list arg) = _sysfatalimpl;
void
jehanne_sysfatal(const char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
(*_sysfatal)(fmt, arg);
va_end(arg);
jehanne_exits("sysfatal");
}

View File

@@ -0,0 +1,125 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
static struct
{
int fd;
int consfd;
char *name;
Dir *d;
Dir *consd;
Lock;
} sl =
{
-1, -1,
};
static void
_syslogopen(void)
{
char buf[1024];
if(sl.fd >= 0)
sys_close(sl.fd);
jehanne_snprint(buf, sizeof(buf), "/sys/log/%s", sl.name);
sl.fd = sys_open(buf, OWRITE|OCEXEC);
}
static int
eqdirdev(Dir *a, Dir *b)
{
return a != nil && b != nil &&
a->dev == b->dev && a->type == b->type &&
a->qid.path == b->qid.path;
}
/*
* Print
* sysname: time: mesg
* on /sys/log/logname.
* If cons or log file can't be opened, print on the system console, too.
*/
void
jehanne_syslog(int cons, const char *logname, const char *fmt, ...)
{
char buf[1024];
char *ctim, *p;
va_list arg;
int n;
Dir *d;
char err[ERRMAX];
err[0] = '\0';
sys_errstr(err, sizeof err);
jehanne_lock(&sl);
/*
* paranoia makes us stat to make sure a fork+close
* hasn't broken our fd's
*/
d = jehanne_dirfstat(sl.fd);
if(sl.fd < 0 || sl.name == nil || jehanne_strcmp(sl.name, logname) != 0 ||
!eqdirdev(d, sl.d)){
jehanne_free(sl.name);
sl.name = jehanne_strdup(logname);
if(sl.name == nil)
cons = 1;
else{
jehanne_free(sl.d);
sl.d = nil;
_syslogopen();
if(sl.fd < 0)
cons = 1;
else
sl.d = jehanne_dirfstat(sl.fd);
}
}
jehanne_free(d);
if(cons){
d = jehanne_dirfstat(sl.consfd);
if(sl.consfd < 0 || !eqdirdev(d, sl.consd)){
jehanne_free(sl.consd);
sl.consd = nil;
sl.consfd = sys_open("#c/cons", OWRITE|OCEXEC);
if(sl.consfd >= 0)
sl.consd = jehanne_dirfstat(sl.consfd);
}
jehanne_free(d);
}
if(fmt == nil){
jehanne_unlock(&sl);
return;
}
ctim = jehanne_ctime(jehanne_time(0));
p = buf + jehanne_snprint(buf, sizeof(buf)-1, "%s ", jehanne_sysname());
jehanne_strncpy(p, ctim+4, 15);
p += 15;
*p++ = ' ';
sys_errstr(err, sizeof err);
va_start(arg, fmt);
p = jehanne_vseprint(p, buf+sizeof(buf)-1, fmt, arg);
va_end(arg);
*p++ = '\n';
n = p - buf;
if(sl.fd >= 0){
sys_seek(sl.fd, 0, 2);
jehanne_write(sl.fd, buf, n);
}
if(cons && sl.consfd >=0)
jehanne_write(sl.consfd, buf, n);
jehanne_unlock(&sl);
}

View File

@@ -0,0 +1,30 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
char*
jehanne_sysname(void)
{
int f, n;
static char b[128];
if(b[0])
return b;
f = sys_open("#c/sysname", OREAD);
if(f >= 0) {
n = jehanne_read(f, b, sizeof(b)-1);
if(n > 0)
b[n] = 0;
sys_close(f);
}
return b;
}

View File

@@ -0,0 +1,22 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int32_t
jehanne_time(int32_t *tp)
{
int64_t t;
t = jehanne_nsec()/1000000000LL;
if(tp != nil)
*tp = t;
return t;
}

View File

@@ -0,0 +1,69 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
static
char*
skip(char *p)
{
while(*p == ' ')
p++;
while(*p != ' ' && *p != 0)
p++;
return p;
}
/*
* after a fork with fd's copied, both fd's are pointing to
* the same Chan structure. Since the offset is kept in the Chan
* structure, the seek's and read's in the two processes can be
* are competing moving the offset around. Hence the unusual loop
* in the middle of this routine.
*/
int32_t
jehanne_times(int32_t *t)
{
char b[200], *p;
static int f = -1;
int i, retries;
uint32_t r = -1;
jehanne_memset(b, 0, sizeof(b));
for(retries = 0; retries < 100; retries++){
if(f < 0)
f = sys_open("/dev/cputime", OREAD|OCEXEC);
if(f < 0)
break;
if(sys_seek(f, 0, 0) < 0 || (i = jehanne_read(f, b, sizeof(b))) < 0){
sys_close(f);
f = -1;
} else {
if(i != 0)
break;
}
}
p = b;
if(t)
t[0] = jehanne_atol(p);
p = skip(p);
if(t)
t[1] = jehanne_atol(p);
p = skip(p);
r = jehanne_atol(p);
if(t){
p = skip(p);
t[2] = jehanne_atol(p);
p = skip(p);
t[3] = jehanne_atol(p);
}
return r;
}

View File

@@ -0,0 +1,205 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#define TZSIZE 150
static void readtimezone(void);
static int rd_name(char**, char*);
static int rd_long(char**, int32_t*);
static
struct
{
char stname[4];
char dlname[4];
int32_t stdiff;
int32_t dldiff;
int32_t dlpairs[TZSIZE];
} timezone;
#define SEC2MIN 60L
#define SEC2HOUR (60L*SEC2MIN)
#define SEC2DAY (24L*SEC2HOUR)
/*
* days per month plus days/year
*/
static int dmsize[] =
{
365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static int ldmsize[] =
{
366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/*
* return the days/month for the given year
*/
static int *
yrsize(int y)
{
if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
return ldmsize;
else
return dmsize;
}
/*
* compute seconds since Jan 1 1970 GMT
* and convert to our timezone.
*/
int32_t
jehanne_tm2sec(Tm *tm)
{
int32_t secs;
int i, yday, year, *d2m;
if(jehanne_strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
readtimezone();
secs = 0;
/*
* seconds per year
*/
year = tm->year + 1900;
for(i = 1970; i < year; i++){
d2m = yrsize(i);
secs += d2m[0] * SEC2DAY;
}
/*
* if mday is set, use mon and mday to compute yday
*/
if(tm->mday){
yday = 0;
d2m = yrsize(year);
for(i=0; i<tm->mon; i++)
yday += d2m[i+1];
yday += tm->mday-1;
}else{
yday = tm->yday;
}
secs += yday * SEC2DAY;
/*
* hours, minutes, seconds
*/
secs += tm->hour * SEC2HOUR;
secs += tm->min * SEC2MIN;
secs += tm->sec;
/*
* Only handles zones mentioned in /env/timezone,
* but things get too ambiguous otherwise.
*/
if(jehanne_strcmp(tm->zone, timezone.stname) == 0)
secs -= timezone.stdiff;
else if(jehanne_strcmp(tm->zone, timezone.dlname) == 0)
secs -= timezone.dldiff;
if(secs < 0)
secs = 0;
return secs;
}
static
void
readtimezone(void)
{
char buf[TZSIZE*11+30], *p;
int i;
jehanne_memset(buf, 0, sizeof(buf));
i = sys_open("/env/timezone", OREAD);
if(i < 0)
goto error;
if(jehanne_read(i, buf, sizeof(buf)) >= sizeof(buf)){
sys_close(1);
goto error;
}
sys_close(i);
p = buf;
if(rd_name(&p, timezone.stname))
goto error;
if(rd_long(&p, &timezone.stdiff))
goto error;
if(rd_name(&p, timezone.dlname))
goto error;
if(rd_long(&p, &timezone.dldiff))
goto error;
for(i=0; i<TZSIZE; i++) {
if(rd_long(&p, &timezone.dlpairs[i]))
goto error;
if(timezone.dlpairs[i] == 0)
return;
}
error:
timezone.stdiff = 0;
jehanne_strcpy(timezone.stname, "GMT");
timezone.dlpairs[0] = 0;
}
static int
rd_name(char **f, char *p)
{
int c, i;
for(;;) {
c = *(*f)++;
if(c != ' ' && c != '\n')
break;
}
for(i=0; i<3; i++) {
if(c == ' ' || c == '\n')
return 1;
*p++ = c;
c = *(*f)++;
}
if(c != ' ' && c != '\n')
return 1;
*p = 0;
return 0;
}
static int
rd_long(char **f, int32_t *p)
{
int c, s;
int32_t l;
s = 0;
for(;;) {
c = *(*f)++;
if(c == '-') {
s++;
continue;
}
if(c != ' ' && c != '\n')
break;
}
if(c == 0) {
*p = 0;
return 0;
}
l = 0;
for(;;) {
if(c == ' ' || c == '\n')
break;
if(c < '0' || c > '9')
return 1;
l = l*10 + c-'0';
c = *(*f)++;
}
if(s)
l = -l;
*p = l;
return 0;
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
uint32_t
jehanne_truerand(void)
{
uint32_t x;
static int randfd = -1;
if(randfd < 0)
randfd = sys_open("/dev/random", OREAD|OCEXEC);
if(randfd < 0)
jehanne_sysfatal("can't open /dev/random");
if(jehanne_read(randfd, &x, sizeof(x)) != sizeof(x))
jehanne_sysfatal("can't read /dev/random");
return x;
}

View File

@@ -0,0 +1,36 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
int
jehanne_tsemacquire(int* addr, long ms)
{
long wkup;
wkup = sys_awake(ms);
while(sys_semacquire(addr, 1) < 0){
if(jehanne_awakened(wkup)){
/* copy canlock semantic for return values */
return 0;
}
/* interrupted; try again */
}
jehanne_forgivewkp(wkup);
return 1;
}

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
Waitmsg*
jehanne_wait(void)
{
int n, l;
char buf[512], *fld[5];
Waitmsg *w;
n = sys_await(buf, sizeof buf-1);
if(n < 0)
return nil;
buf[n] = '\0';
if(jehanne_tokenize(buf, fld, nelem(fld)) != nelem(fld)){
jehanne_werrstr("couldn't parse wait message");
return nil;
}
l = jehanne_strlen(fld[4])+1;
w = jehanne_malloc(sizeof(Waitmsg)+l);
if(w == nil)
return nil;
w->pid = jehanne_atoi(fld[0]);
w->time[0] = jehanne_atoi(fld[1]);
w->time[1] = jehanne_atoi(fld[2]);
w->time[2] = jehanne_atoi(fld[3]);
jehanne_memmove(w->msg, fld[4], l);
return w;
}

View File

@@ -0,0 +1,30 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <9P2000.h>
int
jehanne_waitpid(void)
{
int n;
char buf[512], *fld[5];
n = sys_await(buf, sizeof buf-1);
if(n <= 0)
return -1;
buf[n] = '\0';
if(jehanne_tokenize(buf, fld, nelem(fld)) != nelem(fld)){
jehanne_werrstr("couldn't parse wait message");
return -1;
}
return jehanne_atoi(fld[0]);
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
void
jehanne_werrstr(const char *fmt, ...)
{
va_list arg;
char buf[ERRMAX];
va_start(arg, fmt);
jehanne_vseprint(buf, buf+ERRMAX, fmt, arg);
va_end(arg);
sys_errstr(buf, ERRMAX);
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
long
jehanne_write(int fd, const void* buf, int nbytes)
{
return sys_pwrite(fd, buf, nbytes, ~0LL);
}

View File

@@ -0,0 +1,8 @@
#include <u.h>
#include <libc.h>
void (*_abort)(void);
char *argv0;
int32_t _mainpid;
char *_privates;
char *_nprivates;

View File

@@ -0,0 +1,38 @@
.text
.globl jehanne_ainc /* long ainc(long *); */
/* N.B.: long in Plan 9 is 32 BITS! */
jehanne_ainc:
pushq %rcx
ainclp:
movl (%rdi), %eax
movl %eax, %ecx
incl %ecx /* new */
lock; cmpxchgl %ecx, (%rdi)
jnz ainclp
movl %ecx, %eax
popq %rcx
ret
.globl jehanne_adec /* long adec(long*); */
jehanne_adec:
pushq %rcx
adeclp:
movl (%rdi), %eax
movl %eax, %ecx
decl %ecx /* new */
lock; cmpxchgl %ecx, (%rdi)
jnz adeclp
movl %ecx, %eax
popq %rcx
ret
/*
* void mfence(void);
*/
.globl jehanne_mfence
jehanne_mfence:
mfence
ret

View File

@@ -0,0 +1,41 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
.text
.globl _main
_main:
/* clear the frame pointer */
xorq %rbp, %rbp
/* set argc (%rdi) and argv (%rsi) */
movq 0(%rsp), %rdi
leaq 8(%rsp), %rsi
/* set _mainpid */
movl %r12d, _mainpid
movq %rsp, %rbp
pushq %rsi
pushq %rdi
call _init
popq %rdi
popq %rsi
call __jehanne_libc_init
.size _main,.-_main

View File

@@ -0,0 +1,30 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
.section .init
.global _init
_init:
push %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */
.section .fini
.global _fini
_fini:
push %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */

View File

@@ -0,0 +1,7 @@
.section .init
popq %rbp
ret
.section .fini
popq %rbp
ret

View File

@@ -0,0 +1,11 @@
#include <u.h>
#include <libc.h>
void _cycles(uint64_t *x)
{
uint32_t a, d;
asm __volatile__ ("rdtsc" : "=a" (a), "=d" (d));
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
.text
.globl jehanne_setfcr
jehanne_setfcr:
xor $(0x3F<<7), %edi
and $0xFFC0, %edi
wait
stmxcsr -8(%rsp)
mov -8(%rsp), %eax
and $~0x3F, %eax
or %edi, %eax
mov %eax, -8(%rsp)
ldmxcsr -8(%rsp)
ret
.globl jehanne_getfcr
jehanne_getfcr:
wait
stmxcsr -8(%rsp)
movzx -8(%rsp), %ax
andl $0xFFC0, %eax
xorl $(0x3F<<7), %eax
ret
.globl jehanne_getfsr
jehanne_getfsr:
wait
stmxcsr -8(%rsp)
mov -8(%rsp), %eax
and $0x3F, %eax
RET
.globl jehanne_setfsr
jehanne_setfsr:
and $0x3F, %edi
wait
stmxcsr -8(%rsp)
mov -8(%rsp), %eax
and $~0x3F, %eax
or %edi, %eax
mov %eax, -8(%rsp)
ldmxcsr -8(%rsp)
ret

View File

@@ -0,0 +1,56 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2017-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
extern void **_privates;
extern int _nprivates;
static void call_main(int argc, char *argv[]) __attribute__((noreturn));
/* can be overwritten to do anything before calling main */
extern void __libc_init(int argc, char *argv[]) __attribute__((weak, noreturn));
void
__jehanne_libc_init(int argc, char *argv[])
{
/* Initialize per process structures on the stack */
void *privates[NPRIVATES];
_nprivates = NPRIVATES;
for(_nprivates = 0; _nprivates < NPRIVATES; ++_nprivates)
privates[_nprivates] = nil;
_privates = privates;
if(__libc_init != nil)
__libc_init(argc, argv);
call_main(argc, argv);
}
static void
call_main(int argc, char *argv[])
{
extern void main(int argc, char *argv[]) __attribute__((weak));
if(main != nil){
main(argc, argv);
jehanne_exits("main");
} else {
jehanne_exits("missing main");
}
}

View File

@@ -0,0 +1,16 @@
#include <u.h>
#include <libc.h>
#include <ureg.h>
void
jehanne_notejmp(void *vr, jmp_buf j, int ret)
{
struct Ureg *r = vr;
r->ax = ret;
if(ret == 0)
r->ax = 1;
r->ip = j[JMPBUFPC];
r->sp = j[JMPBUFSP] + 8;
sys_noted(NCONT);
}

View File

@@ -0,0 +1,9 @@
#include <u.h>
#include <libc.h>
void
rdpmc (int counter, int low, int high)
{
asm __volatile__("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)) ;
}

View File

@@ -0,0 +1,39 @@
.text
.globl jehanne_longjmp
jehanne_longjmp:
movq %rdi, %r9
movq 0(%r9), %rbx
movq 8(%r9), %r12
movq 16(%r9), %r13
movq 24(%r9), %r14
movq 32(%r9), %r15
movq 40(%r9), %rbp
movq 48(%r9), %rsp
movl %esi, %eax
test %eax, %eax /* if val != 0 */
jnz 1f /* return val */
incl %eax /* else return 1 */
1:
movq 56(%r9), %r8 /* return to caller of setjmp */
movq 64(%r9), %rdi /* 1st function argument */
movq 72(%r9), %rsi /* 2nd function argument */
jmp *%r8
.globl jehanne_setjmp
jehanne_setjmp:
movq %rbx, 0(%rdi)
movq %r12, 8(%rdi)
movq %r13, 16(%rdi)
movq %r14, 24(%rdi)
movq %r15, 32(%rdi)
movq %rbp, 40(%rdi)
popq %rdx /* return address */
movq %rsp, 48(%rdi)
movq %rdx, 56(%rdi)
xorl %eax, %eax /* return 0 */
jmp *%rdx

View File

@@ -0,0 +1,5 @@
.globl jehanne_sqrt
jehanne_sqrt:
sqrtsd %xmm0, %xmm0
ret

View File

@@ -0,0 +1,43 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <libc.h>
#if defined(__SSP__) || defined(__SSP_ALL__) || defined(__SSP_STRONG__)
#define STACK_CHK_GUARD 0x2101170925071803
uintptr_t __stack_chk_guard = STACK_CHK_GUARD;
__attribute__((noreturn))
void
__stack_chk_fail(void)
{
#if __STDC_HOSTED__
abort();
__builtin_unreachable();
#else
extern void panic(char *fmt, ...) __attribute__ ((noreturn));
panic("Stack smashing detected");
#endif
}
#else /* no stack protector */
uintptr_t __stack_chk_guard = 0;
#endif

View File

@@ -0,0 +1,10 @@
/*
* The kernel and the libc use the same constant for TAS
*/
.text
.globl jehanne__tas
jehanne__tas:
movl $0xdeaddead, %eax
xchgl %eax, 0(%rdi) /* lock->key */
ret

View File

@@ -0,0 +1,27 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <apw/stdlib.h>
void
abort(void)
{
if(_abort)
_abort();
else
while(*(int*)0);
}

View File

@@ -0,0 +1,55 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <apw/stdlib.h>
div_t
div(int num, int denom)
{
div_t r;
r.quot = num / denom;
r.rem = num % denom;
if (num >= 0 && r.rem < 0) {
++r.quot;
r.rem -= denom;
}
else if (num < 0 && r.rem > 0) {
--r.quot;
r.rem += denom;
}
return (r);
}
ldiv_t
ldiv(long num, long denom)
{
ldiv_t r;
r.quot = num / denom;
r.rem = num % denom;
if (num >= 0 && r.rem < 0) {
++r.quot;
r.rem -= denom;
}
else if (num < 0 && r.rem > 0) {
--r.quot;
r.rem += denom;
}
return (r);
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <apw/stdlib.h>
void
__eprintf(const char *format, const char *file, unsigned int line, const char *expression)
{
jehanne_fprint(2, format, file, line, expression);
abort();
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <apw/stdlib.h>
void
exit(int status)
{
if(status == EXIT_SUCCESS)
jehanne_exits(nil);
jehanne_exits(jehanne_smprint(__POSIX_EXIT_PREFIX "%d", status));
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <apw/stdlib.h>
int
system(const char *command)
{
pid_t pid;
Waitmsg *w;
int ret = 0;
char *s = nil;
if(command == nil)
return jehanne_access("/cmd/rc", AEXEC) == 0;
switch(pid = sys_rfork(RFFDG|RFREND|RFPROC|RFENVG|RFNOTEG)){
case -1:
return -1;
case 0:
jehanne_execl("/cmd/rc", "rc", "-c", command, nil);
jehanne_sysfatal("execl returned");
return -1;
default:
// TODO: fix this http://man7.org/linux/man-pages/man3/system.3.html
while((w = jehanne_wait()) && w->pid != pid)
jehanne_free(w);
if(w == nil)
return -1;
if(w->msg[0] != 0){
s = jehanne_strstr(w->msg, __POSIX_EXIT_PREFIX);
if(s){
s += (sizeof(__POSIX_EXIT_PREFIX)/sizeof(char) - 1);
ret = jehanne_atoi(s);
} else
ret = 127;
}
jehanne_free(w);
return ret;
}
}

View File

@@ -0,0 +1,246 @@
{
"CrtFiles": {
"Cflags": [
"-fasm"
],
"Include": [
"../lib.json"
],
"Post": [
"cp crt0.o crti.o crtn.o $JEHANNE/arch/$ARCH/lib/"
],
"SourceFiles": [
"$ARCH/crt0.s",
"$ARCH/crti.s",
"$ARCH/crtn.s"
]
},
"LibJehanne": {
"Cflags": [
"-fasm"
],
"Include": [
"../lib.json"
],
"Install": "/arch/$ARCH/lib/",
"Library": "libjehanne.a",
"Pre": [
"usyscalls header $JEHANNE/sys/src/sysconf.json > $JEHANNE/arch/amd64/include/syscalls.h",
"usyscalls code $JEHANNE/sys/src/sysconf.json > $JEHANNE/sys/src/lib/jehanne/amd64/syscalls.c"
],
"Post": [
"TOOL=ar; $TOOLPREFIX$TOOL rcs $JEHANNE/arch/$ARCH/lib/libssp.a",
"TOOL=ar; $TOOLPREFIX$TOOL rcs $JEHANNE/arch/$ARCH/lib/libssp_nonshared.a"
],
"SourceFiles": [
"9sys/access.c",
"9sys/announce.c",
"9sys/awakened.c",
"9sys/chdir.c",
"9sys/convD2M.c",
"9sys/convM2D.c",
"9sys/cputime.c",
"9sys/ctime.c",
"9sys/dial.c",
"9sys/dirfstat.c",
"9sys/dirfwstat.c",
"9sys/dirmodefmt.c",
"9sys/dirread.c",
"9sys/dirstat.c",
"9sys/dirwstat.c",
"9sys/dup.c",
"9sys/fork.c",
"9sys/getnetconninfo.c",
"9sys/getenv.c",
"9sys/getmainpid.c",
"9sys/getpid.c",
"9sys/getppid.c",
"9sys/getwd.c",
"9sys/iounit.c",
"9sys/nsec.c",
"9sys/nulldir.c",
"9sys/ocreate.c",
"9sys/pexec.c",
"9sys/pipe.c",
"9sys/postnote.c",
"9sys/privalloc.c",
"9sys/pushssl.c",
"9sys/putenv.c",
"9sys/qlock.c",
"9sys/read.c",
"9sys/rerrstr.c",
"9sys/segbrk.c",
"9sys/segattach.c",
"9sys/setnetmtpt.c",
"9sys/sleep.c",
"9sys/stat.c",
"9sys/sysfatal.c",
"9sys/syslog.c",
"9sys/sysname.c",
"9sys/time.c",
"9sys/times.c",
"9sys/tm2sec.c",
"9sys/truerand.c",
"9sys/tsemacquire.c",
"9sys/wait.c",
"9sys/waitpid.c",
"9sys/werrstr.c",
"9sys/write.c",
"apw/abort.c",
"apw/div.c",
"apw/eprintf.c",
"apw/exit.c",
"apw/system.c",
"fmt/dofmt.c",
"fmt/dorfmt.c",
"fmt/errfmt.c",
"fmt/fltfmt.c",
"fmt/fmt.c",
"fmt/fmtfd.c",
"fmt/fmtlock.c",
"fmt/fmtprint.c",
"fmt/fmtquote.c",
"fmt/fmtrune.c",
"fmt/fmtstr.c",
"fmt/fmtvprint.c",
"fmt/fprint.c",
"fmt/print.c",
"fmt/runefmtstr.c",
"fmt/runeseprint.c",
"fmt/runesmprint.c",
"fmt/runesnprint.c",
"fmt/runesprint.c",
"fmt/runevseprint.c",
"fmt/runevsmprint.c",
"fmt/runevsnprint.c",
"fmt/seprint.c",
"fmt/smprint.c",
"fmt/snprint.c",
"fmt/sprint.c",
"fmt/vfprint.c",
"fmt/vseprint.c",
"fmt/vsmprint.c",
"fmt/vsnprint.c",
"port/_assert.c",
"port/abs.c",
"port/asin.c",
"port/atan.c",
"port/atan2.c",
"port/atexit.c",
"port/atnotify.c",
"port/atof.c",
"port/atol.c",
"port/atoll.c",
"port/bsearch.c",
"port/cistrcmp.c",
"port/cistrncmp.c",
"port/cistrstr.c",
"port/charstod.c",
"port/cleanname.c",
"port/ctype.c",
"port/encodefmt.c",
"port/execl.c",
"port/exits.c",
"port/exp.c",
"port/fabs.c",
"port/floor.c",
"port/fmod.c",
"port/frand.c",
"port/frexp.c",
"port/getcallerpc.c",
"port/getfields.c",
"port/getuser.c",
"port/hangup.c",
"port/hypot.c",
"port/lnrand.c",
"port/lock.c",
"port/log.c",
"port/lrand.c",
"port/malloc.c",
"port/memccpy.c",
"port/memchr.c",
"port/memcmp.c",
"port/memmove.c",
"port/memset.c",
"port/mktemp.c",
"port/muldiv.c",
"port/nan.c",
"port/needsrcquote.c",
"port/netmkaddr.c",
"port/nrand.c",
"port/ntruerand.c",
"port/perror.c",
"port/pool.c",
"port/pow.c",
"port/pow10.c",
"port/qsort.c",
"port/quote.c",
"port/rand.c",
"port/readn.c",
"port/rune.c",
"port/runebase.c",
"port/runebsearch.c",
"port/runestrcat.c",
"port/runestrchr.c",
"port/runestrcmp.c",
"port/runestrcpy.c",
"port/runestrecpy.c",
"port/runestrdup.c",
"port/runestrncat.c",
"port/runestrncmp.c",
"port/runestrncpy.c",
"port/runestrrchr.c",
"port/runestrlen.c",
"port/runestrstr.c",
"port/runetype.c",
"port/sin.c",
"port/sinh.c",
"port/strcat.c",
"port/strchr.c",
"port/strcmp.c",
"port/strcpy.c",
"port/strecpy.c",
"port/strcspn.c",
"port/strdup.c",
"port/strlen.c",
"port/strncat.c",
"port/strncmp.c",
"port/strncpy.c",
"port/strpbrk.c",
"port/strrchr.c",
"port/strspn.c",
"port/strstr.c",
"port/strtod.c",
"port/strtok.c",
"port/strtol.c",
"port/strtoll.c",
"port/strtoul.c",
"port/strtoull.c",
"port/tan.c",
"port/tanh.c",
"port/tokenize.c",
"port/toupper.c",
"port/utfecpy.c",
"port/utflen.c",
"port/utfnlen.c",
"port/utfrune.c",
"port/utfrrune.c",
"port/utfutf.c",
"port/u16.c",
"port/u32.c",
"port/u64.c",
"$ARCH/initlib.c",
"$ARCH/syscalls.c",
"$ARCH/notejmp.c",
"$ARCH/cycles.c",
"$ARCH/argv0.c",
"$ARCH/rdpmc.c",
"$ARCH/setjmp.s",
"$ARCH/stackchk.c",
"$ARCH/getfcr.s",
"$ARCH/sqrt.s",
"$ARCH/tas.s",
"$ARCH/atom.s"
]
}
}

View File

@@ -0,0 +1,539 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/* format the output into f->to and return the number of characters fmted */
int
jehanne_dofmt(Fmt *f, const char *fmt)
{
Rune rune, *rt, *rs;
int r;
char *t, *s;
int n, nfmt;
nfmt = f->nfmt;
for(;;){
if(f->runes){
rt = f->to;
rs = f->stop;
while((r = *(uint8_t*)fmt) && r != '%'){
if(r < Runeself)
fmt++;
else{
fmt += jehanne_chartorune(&rune, fmt);
r = rune;
}
FMTRCHAR(f, rt, rs, r);
}
fmt++;
f->nfmt += rt - (Rune *)f->to;
f->to = rt;
if(!r)
return f->nfmt - nfmt;
f->stop = rs;
}else{
t = f->to;
s = f->stop;
while((r = *(uint8_t*)fmt) && r != '%'){
if(r < Runeself){
FMTCHAR(f, t, s, r);
fmt++;
}else{
n = jehanne_chartorune(&rune, fmt);
if(t + n > s){
t = _fmtflush(f, t, n);
if(t != nil)
s = f->stop;
else
return -1;
}
while(n--)
*t++ = *fmt++;
}
}
fmt++;
f->nfmt += t - (char *)f->to;
f->to = t;
if(!r)
return f->nfmt - nfmt;
f->stop = s;
}
fmt = _fmtdispatch(f, fmt, 0);
if(fmt == nil)
return -1;
}
}
void *
_fmtflush(Fmt *f, void *t, int len)
{
if(f->runes)
f->nfmt += (Rune*)t - (Rune*)f->to;
else
f->nfmt += (char*)t - (char *)f->to;
f->to = t;
if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
f->stop = f->to;
return nil;
}
return f->to;
}
/*
* put a formatted block of memory sz bytes long of n runes into the output buffer,
* left/right justified in a field of at least f->width charactes
*/
int
_fmtpad(Fmt *f, int n)
{
char *t, *s;
int i;
t = f->to;
s = f->stop;
for(i = 0; i < n; i++)
FMTCHAR(f, t, s, ' ');
f->nfmt += t - (char *)f->to;
f->to = t;
return 0;
}
int
_rfmtpad(Fmt *f, int n)
{
Rune *t, *s;
int i;
t = f->to;
s = f->stop;
for(i = 0; i < n; i++)
FMTRCHAR(f, t, s, ' ');
f->nfmt += t - (Rune *)f->to;
f->to = t;
return 0;
}
int
_fmtcpy(Fmt *f, const void *vm, int n, int sz)
{
Rune *rt, *rs, r;
char *t, *s;
const char *m, *me;
uint32_t fl;
int nc, w;
m = vm;
me = m + sz;
w = f->width;
fl = f->flags;
if((fl & FmtPrec) && n > f->prec)
n = f->prec;
if(f->runes){
if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
return -1;
rt = f->to;
rs = f->stop;
for(nc = n; nc > 0; nc--){
r = *(uint8_t*)m;
if(r < Runeself)
m++;
else if((me - m) >= UTFmax || jehanne_fullrune(m, me-m))
m += jehanne_chartorune(&r, m);
else
break;
FMTRCHAR(f, rt, rs, r);
}
f->nfmt += rt - (Rune *)f->to;
f->to = rt;
if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
return -1;
}else{
if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
return -1;
t = f->to;
s = f->stop;
for(nc = n; nc > 0; nc--){
r = *(uint8_t*)m;
if(r < Runeself)
m++;
else if((me - m) >= UTFmax || jehanne_fullrune(m, me-m))
m += jehanne_chartorune(&r, m);
else
break;
FMTRUNE(f, t, s, r);
}
f->nfmt += t - (char *)f->to;
f->to = t;
if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
return -1;
}
return 0;
}
int
_fmtrcpy(Fmt *f, const void *vm, int n)
{
Rune r, *rt, *rs;
const Rune *m, *me;
char *t, *s;
uint32_t fl;
int w;
m = vm;
w = f->width;
fl = f->flags;
if((fl & FmtPrec) && n > f->prec)
n = f->prec;
if(f->runes){
if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
return -1;
rt = f->to;
rs = f->stop;
for(me = m + n; m < me; m++)
FMTRCHAR(f, rt, rs, *m);
f->nfmt += rt - (Rune *)f->to;
f->to = rt;
if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
return -1;
}else{
if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
return -1;
t = f->to;
s = f->stop;
for(me = m + n; m < me; m++){
r = *m;
FMTRUNE(f, t, s, r);
}
f->nfmt += t - (char *)f->to;
f->to = t;
if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
return -1;
}
return 0;
}
/* fmt out one character */
int
_charfmt(Fmt *f)
{
char x[1];
x[0] = va_arg(f->args, int);
f->prec = 1;
return _fmtcpy(f, x, 1, 1);
}
/* fmt out one rune */
int
_runefmt(Fmt *f)
{
Rune x[1];
x[0] = va_arg(f->args, int);
return _fmtrcpy(f, x, 1);
}
/* public helper routine: fmt out a null terminated string already in hand */
int
jehanne_fmtstrcpy(Fmt *f, const char *s)
{
int i, j;
Rune r;
if(!s)
return _fmtcpy(f, "<nil>", 5, 5);
/* if precision is specified, make sure we don't wander off the end */
if(f->flags & FmtPrec){
i = 0;
for(j=0; j<f->prec && s[i]; j++)
i += jehanne_chartorune(&r, s+i);
return _fmtcpy(f, s, j, i);
}
return _fmtcpy(f, s, jehanne_utflen(s), jehanne_strlen(s));
}
/* fmt out a null terminated utf string */
int
_strfmt(Fmt *f)
{
char *s;
s = va_arg(f->args, char *);
return jehanne_fmtstrcpy(f, s);
}
/* public helper routine: fmt out a null terminated rune string already in hand */
int
jehanne_fmtrunestrcpy(Fmt *f, const Rune *s)
{
const Rune *e;
int n, p;
if(!s)
return _fmtcpy(f, "<nil>", 5, 5);
/* if precision is specified, make sure we don't wander off the end */
if(f->flags & FmtPrec){
p = f->prec;
for(n = 0; n < p; n++)
if(s[n] == 0)
break;
}else{
for(e = s; *e; e++)
;
n = e - s;
}
return _fmtrcpy(f, s, n);
}
/* fmt out a null terminated rune string */
int
_runesfmt(Fmt *f)
{
Rune *s;
s = va_arg(f->args, Rune *);
return jehanne_fmtrunestrcpy(f, s);
}
/* fmt a % */
int
_percentfmt(Fmt *f)
{
Rune x[1];
x[0] = f->r;
f->prec = 1;
return _fmtrcpy(f, x, 1);
}
enum {
/* %,#llb could emit a sign, "0b" and 64 digits with 21 commas */
Maxintwidth = 1 + 2 + 64 + 64/3,
};
/* fmt an integer */
int
_ifmt(Fmt *f)
{
char buf[Maxintwidth + 1], *p, *conv;
uint64_t vu;
uint32_t u;
uintptr_t pu;
int neg, base, i, n, fl, w, isv;
neg = 0;
fl = f->flags;
isv = 0;
vu = 0;
u = 0;
if(f->r == 'p'){
pu = va_arg(f->args, uintptr_t);
if(sizeof(uintptr_t) == sizeof(uint64_t)){
vu = pu;
isv = 1;
}else
u = pu;
f->r = 'x';
fl |= FmtUnsigned;
}else if(fl & FmtVLong){
isv = 1;
if(fl & FmtUnsigned)
vu = va_arg(f->args, uint64_t);
else
vu = va_arg(f->args, int64_t);
}else if(fl & FmtLong){
if(fl & FmtUnsigned)
u = va_arg(f->args, uint32_t);
else
u = va_arg(f->args, int32_t);
}else if(fl & FmtByte){
if(fl & FmtUnsigned)
u = (uint8_t)va_arg(f->args, int);
else
u = (char)va_arg(f->args, int);
}else if(fl & FmtShort){
if(fl & FmtUnsigned)
u = (uint16_t)va_arg(f->args, int);
else
u = (int16_t)va_arg(f->args, int);
}else{
if(fl & FmtUnsigned)
u = va_arg(f->args, uint32_t);
else
u = va_arg(f->args, int);
}
conv = "0123456789abcdef";
switch(f->r){
case 'd':
base = 10;
break;
case 'x':
base = 16;
break;
case 'X':
base = 16;
conv = "0123456789ABCDEF";
break;
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
default:
return -1;
}
if(!(fl & FmtUnsigned)){
if(isv && (int64_t)vu < 0){
vu = -(int64_t)vu;
neg = 1;
}else if(!isv && (int32_t)u < 0){
u = -(int32_t)u;
neg = 1;
}
}
p = buf + sizeof buf - 1;
n = 0;
if(isv){
while(vu){
i = vu % base;
vu /= base;
if((fl & FmtComma) && n % 4 == 3){
*p-- = ',';
n++;
}
*p-- = conv[i];
n++;
}
}else{
while(u){
i = u % base;
u /= base;
if((fl & FmtComma) && n % 4 == 3){
*p-- = ',';
n++;
}
*p-- = conv[i];
n++;
}
}
if(n == 0){
*p-- = '0';
n = 1;
}
for(w = f->prec; n < w && p > buf+3; n++)
*p-- = '0';
if(neg || (fl & (FmtSign|FmtSpace)))
n++;
if(fl & FmtSharp){
if(base == 16)
n += 2;
else if(base == 8){
if(p[1] == '0')
fl &= ~FmtSharp;
else
n++;
}
}
if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
for(w = f->width; n < w && p > buf+3; n++)
*p-- = '0';
f->width = 0;
}
if(fl & FmtSharp){
if(base == 16)
*p-- = f->r;
if(base == 16 || base == 8)
*p-- = '0';
}
if(neg)
*p-- = '-';
else if(fl & FmtSign)
*p-- = '+';
else if(fl & FmtSpace)
*p-- = ' ';
f->flags &= ~FmtPrec;
return _fmtcpy(f, p + 1, n, n);
}
int
_countfmt(Fmt *f)
{
void *p;
uint32_t fl;
fl = f->flags;
p = va_arg(f->args, void*);
if(fl & FmtVLong){
*(int64_t*)p = f->nfmt;
}else if(fl & FmtLong){
*(int32_t*)p = f->nfmt;
}else if(fl & FmtByte){
*(char*)p = f->nfmt;
}else if(fl & FmtShort){
*(int16_t*)p = f->nfmt;
}else{
*(int*)p = f->nfmt;
}
return 0;
}
int
_flagfmt(Fmt *f)
{
switch(f->r){
case ',':
f->flags |= FmtComma;
break;
case '-':
f->flags |= FmtLeft;
break;
case '+':
f->flags |= FmtSign;
break;
case '#':
f->flags |= FmtSharp;
break;
case ' ':
f->flags |= FmtSpace;
break;
case 'u':
f->flags |= FmtUnsigned;
break;
case 'h':
if(f->flags & FmtShort)
f->flags |= FmtByte;
f->flags |= FmtShort;
break;
case 'l':
if(f->flags & FmtLong)
f->flags |= FmtVLong;
f->flags |= FmtLong;
break;
}
return 1;
}
/* default error format */
int
_badfmt(Fmt *f)
{
Rune x[3];
x[0] = '%';
x[1] = f->r;
x[2] = '%';
f->prec = 3;
_fmtrcpy(f, x, 3);
return 0;
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/* format the output into f->to and return the number of characters fmted */
int
jehanne_dorfmt(Fmt *f, const Rune *fmt)
{
Rune *rt, *rs;
int r;
char *t, *s;
int nfmt;
nfmt = f->nfmt;
for(;;){
if(f->runes){
rt = f->to;
rs = f->stop;
while((r = *fmt++) && r != '%'){
FMTRCHAR(f, rt, rs, r);
}
f->nfmt += rt - (Rune *)f->to;
f->to = rt;
if(!r)
return f->nfmt - nfmt;
f->stop = rs;
}else{
t = f->to;
s = f->stop;
while((r = *fmt++) && r != '%'){
FMTRUNE(f, t, f->stop, r);
}
f->nfmt += t - (char *)f->to;
f->to = t;
if(!r)
return f->nfmt - nfmt;
f->stop = s;
}
fmt = _fmtdispatch(f, fmt, 1);
if(fmt == nil)
return -1;
}
}

View File

@@ -0,0 +1,26 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
int
jehanne_errfmt(Fmt *f)
{
#if __STDC_HOSTED__
char buf[ERRMAX+1];
jehanne_rerrstr(buf, ERRMAX);
return _fmtcpy(f, buf, jehanne_utflen(buf), jehanne_strlen(buf));
#else
extern void panic(char *fmt, ...) __attribute__ ((noreturn));
panic("No errfmt in kernel");
#endif
}

View File

@@ -0,0 +1,327 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include "fmtdef.h"
enum
{
FDIGIT = 30,
FDEFLT = 6,
NSIGNIF = 17,
NEXP10 = 308,
};
static int
xadd(char *a, int n, int v)
{
char *b;
int c;
if(n < 0 || n >= NSIGNIF)
return 0;
for(b = a+n; b >= a; b--) {
c = *b + v;
if(c <= '9') {
*b = c;
return 0;
}
*b = '0';
v = 1;
}
*a = '1'; // overflow adding
return 1;
}
static int
xsub(char *a, int n, int v)
{
char *b;
int c;
for(b = a+n; b >= a; b--) {
c = *b - v;
if(c >= '0') {
*b = c;
return 0;
}
*b = '9';
v = 1;
}
*a = '9'; // underflow subtracting
return 1;
}
static void
xdtoa(Fmt *fmt, char *s2, double f)
{
char s1[NSIGNIF+10];
double g, h;
int e, d, i, n;
int c1, c2, c3, c4, ucase, sign, chr, prec;
prec = FDEFLT;
if(fmt->flags & FmtPrec)
prec = fmt->prec;
if(prec > FDIGIT)
prec = FDIGIT;
if(jehanne_isNaN(f)) {
jehanne_strcpy(s2, "NaN");
return;
}
if(jehanne_isInf(f, 1)) {
jehanne_strcpy(s2, "+Inf");
return;
}
if(jehanne_isInf(f, -1)) {
jehanne_strcpy(s2, "-Inf");
return;
}
sign = 0;
if(f < 0) {
f = -f;
sign++;
}
ucase = 0;
chr = fmt->r;
if(isupper(chr)) {
ucase = 1;
chr = jehanne_tolower(chr);
}
e = 0;
g = f;
if(g != 0) {
jehanne_frexp(f, &e);
e = e * .301029995664;
if(e >= -150 && e <= +150) {
d = 0;
h = f;
} else {
d = e/2;
h = f * jehanne_pow10(-d);
}
g = h * jehanne_pow10(d-e);
while(g < 1) {
e--;
g = h * jehanne_pow10(d-e);
}
while(g >= 10) {
e++;
g = h * jehanne_pow10(d-e);
}
}
/*
* convert NSIGNIF digits and convert
* back to get accuracy.
*/
for(i=0; i<NSIGNIF; i++) {
d = g;
s1[i] = d + '0';
g = (g - d) * 10;
}
s1[i] = 0;
/*
* try decimal rounding to eliminate 9s
*/
c2 = prec + 1;
if(chr == 'f')
c2 += e;
if(c2 >= NSIGNIF-2) {
jehanne_strcpy(s2, s1);
d = e;
s1[NSIGNIF-2] = '0';
s1[NSIGNIF-1] = '0';
jehanne_sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
g = jehanne_strtod(s1, nil);
if(g == f)
goto found;
if(xadd(s1, NSIGNIF-3, 1)) {
e++;
jehanne_sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
}
g = jehanne_strtod(s1, nil);
if(g == f)
goto found;
jehanne_strcpy(s1, s2);
e = d;
}
/*
* convert back so s1 gets exact answer
*/
for(;;) {
jehanne_sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
g = jehanne_strtod(s1, nil);
if(f > g) {
if(xadd(s1, NSIGNIF-1, 1))
e--;
continue;
}
if(f < g) {
if(xsub(s1, NSIGNIF-1, 1))
e++;
continue;
}
break;
}
found:
/*
* sign
*/
d = 0;
i = 0;
if(sign)
s2[d++] = '-';
else if(fmt->flags & FmtSign)
s2[d++] = '+';
else if(fmt->flags & FmtSpace)
s2[d++] = ' ';
/*
* copy into final place
* c1 digits of leading '0'
* c2 digits from conversion
* c3 digits of trailing '0'
* c4 digits after '.'
*/
c1 = 0;
c2 = prec + 1;
c3 = 0;
c4 = prec;
switch(chr) {
default:
if(xadd(s1, c2, 5))
e++;
break;
case 'g':
/*
* decide on 'e' of 'f' style convers
*/
if(xadd(s1, c2, 5))
e++;
if(e >= -5 && e <= prec) {
c1 = -e - 1;
c4 = prec - e;
chr = 'h'; // flag for 'f' style
}
break;
case 'f':
if(xadd(s1, c2+e, 5))
e++;
c1 = -e;
if(c1 > prec)
c1 = c2;
c2 += e;
break;
}
/*
* clean up c1 c2 and c3
*/
if(c1 < 0)
c1 = 0;
if(c2 < 0)
c2 = 0;
if(c2 > NSIGNIF) {
c3 = c2-NSIGNIF;
c2 = NSIGNIF;
}
/*
* copy digits
*/
while(c1 > 0) {
if(c1+c2+c3 == c4)
s2[d++] = '.';
s2[d++] = '0';
c1--;
}
while(c2 > 0) {
if(c2+c3 == c4)
s2[d++] = '.';
s2[d++] = s1[i++];
c2--;
}
while(c3 > 0) {
if(c3 == c4)
s2[d++] = '.';
s2[d++] = '0';
c3--;
}
/*
* strip trailing '0' on g conv
*/
if(fmt->flags & FmtSharp) {
if(0 == c4)
s2[d++] = '.';
} else
if(chr == 'g' || chr == 'h') {
for(n=d-1; n>=0; n--)
if(s2[n] != '0')
break;
for(i=n; i>=0; i--)
if(s2[i] == '.') {
d = n;
if(i != n)
d++;
break;
}
}
if(chr == 'e' || chr == 'g') {
if(ucase)
s2[d++] = 'E';
else
s2[d++] = 'e';
c1 = e;
if(c1 < 0) {
s2[d++] = '-';
c1 = -c1;
} else
s2[d++] = '+';
if(c1 >= 100) {
s2[d++] = c1/100 + '0';
c1 = c1%100;
}
s2[d++] = c1/10 + '0';
s2[d++] = c1%10 + '0';
}
s2[d] = 0;
}
int
_floatfmt(Fmt *fmt, double f)
{
char s[1+NEXP10+1+FDIGIT+1];
/*
* The max length of a %f string is
* '[+-]'+"max exponent"+'.'+"max precision"+'\0'
* which is 341 currently.
*/
xdtoa(fmt, s, f);
fmt->flags &= FmtWidth|FmtLeft;
_fmtcpy(fmt, s, jehanne_strlen(s), jehanne_strlen(s));
return 0;
}
int
_efgfmt(Fmt *f)
{
double d;
d = va_arg(f->args, double);
return _floatfmt(f, d);
}

View File

@@ -0,0 +1,227 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
enum
{
Maxfmt = 64
};
typedef struct Convfmt Convfmt;
struct Convfmt
{
int c;
volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
};
struct
{
/* lock by calling _fmtlock, _fmtunlock */
int nfmt;
Convfmt fmt[Maxfmt];
} fmtalloc;
static Convfmt knownfmt[] = {
' ', _flagfmt,
'#', _flagfmt,
'%', _percentfmt,
'+', _flagfmt,
',', _flagfmt,
'-', _flagfmt,
'C', _runefmt,
'E', _efgfmt,
'G', _efgfmt,
'S', _runesfmt,
'X', _ifmt,
'b', _ifmt,
'c', _charfmt,
'd', _ifmt,
'e', _efgfmt,
'f', _efgfmt,
'g', _efgfmt,
'h', _flagfmt,
'l', _flagfmt,
'n', _countfmt,
'o', _ifmt,
'p', _ifmt,
'r', jehanne_errfmt,
's', _strfmt,
'u', _flagfmt,
'x', _ifmt,
0, nil,
};
int (*doquote)(int);
/*
* _fmtlock() must be set
*/
static int
_fmtinstall(int c, Fmts f)
{
Convfmt *p, *ep;
if(c<=0 || c>=65536)
return -1;
if(!f)
f = _badfmt;
ep = &fmtalloc.fmt[fmtalloc.nfmt];
for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c)
break;
if(p == &fmtalloc.fmt[Maxfmt])
return -1;
p->fmt = f;
if(p == ep){ /* installing a new format character */
fmtalloc.nfmt++;
p->c = c;
}
return 0;
}
int
jehanne_fmtinstall(int c, Fmts f)
{
int ret;
_fmtlock();
ret = _fmtinstall(c, f);
_fmtunlock();
return ret;
}
static Fmts
fmtfmt(int c)
{
Convfmt *p, *ep;
ep = &fmtalloc.fmt[fmtalloc.nfmt];
for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c){
while(p->fmt == nil) /* loop until value is updated */
;
return p->fmt;
}
/* is this a predefined format char? */
_fmtlock();
for(p=knownfmt; p->c; p++)
if(p->c == c){
_fmtinstall(p->c, p->fmt);
_fmtunlock();
return p->fmt;
}
_fmtunlock();
return _badfmt;
}
void*
_fmtdispatch(Fmt *f, const void *fmt, int isrunes)
{
Rune rune, r;
int i, n, w, p;
uint32_t fl;
const void *ret;
w = f->width;
p = f->prec;
fl = f->flags;
f->flags = 0;
f->width = f->prec = 0;
for(;;){
if(isrunes){
r = *(Rune*)fmt;
fmt = (Rune*)fmt + 1;
}else{
fmt = (char*)fmt + jehanne_chartorune(&rune, fmt);
r = rune;
}
f->r = r;
switch(r){
case '\0':
ret = nil;
goto end;
case '.':
f->flags |= FmtWidth|FmtPrec;
continue;
case '0':
if(!(f->flags & FmtWidth)){
f->flags |= FmtZero;
continue;
}
/* fall through */
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
i = 0;
while(r >= '0' && r <= '9'){
i = i * 10 + r - '0';
if(isrunes){
r = *(Rune*)fmt;
fmt = (Rune*)fmt + 1;
}else{
r = *(char*)fmt;
fmt = (char*)fmt + 1;
}
}
if(isrunes)
fmt = (Rune*)fmt - 1;
else
fmt = (char*)fmt - 1;
numflag:
if(f->flags & FmtWidth){
f->flags |= FmtPrec;
f->prec = i;
}else{
f->flags |= FmtWidth;
f->width = i;
}
continue;
case '*':
i = va_arg(f->args, int);
if(i < 0){
/*
* negative precision =>
* ignore the precision.
*/
if(f->flags & FmtPrec){
f->flags &= ~FmtPrec;
f->prec = 0;
continue;
}
i = -i;
f->flags |= FmtLeft;
}
goto numflag;
}
n = (*fmtfmt(r))(f);
if(n < 0){
ret = nil;
break;
}
if(n == 0){
ret = fmt;
break;
}
}
end:
f->width = w;
f->prec = p;
f->flags = fl;
return (void*)ret;
}

View File

@@ -0,0 +1,94 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* dofmt -- format to a buffer
* the number of characters formatted is returned,
* or -1 if there was an error.
* if the buffer is ever filled, flush is called.
* it should reset the buffer and return whether formatting should continue.
*/
typedef int (*Fmts)(Fmt*);
typedef struct Quoteinfo Quoteinfo;
struct Quoteinfo
{
int quoted; /* if set, string must be quoted */
int nrunesin; /* number of input runes that can be accepted */
int nbytesin; /* number of input bytes that can be accepted */
int nrunesout; /* number of runes that will be generated */
int nbytesout; /* number of bytes that will be generated */
};
void *_fmtflush(Fmt*, void*, int);
void *_fmtdispatch(Fmt*, const void*, int);
int _floatfmt(Fmt*, double);
int _fmtpad(Fmt*, int);
int _rfmtpad(Fmt*, int);
int _fmtFdFlush(Fmt*);
int _efgfmt(Fmt*);
int _charfmt(Fmt*);
int _countfmt(Fmt*);
int _flagfmt(Fmt*);
int _percentfmt(Fmt*);
int _ifmt(Fmt*);
int _runefmt(Fmt*);
int _runesfmt(Fmt*);
int _strfmt(Fmt*);
int _badfmt(Fmt*);
int _fmtcpy(Fmt*, const void*, int, int);
int _fmtrcpy(Fmt*, const void*, int n);
void _fmtlock(void);
void _fmtunlock(void);
#define FMTCHAR(f, t, s, c)\
do{\
if(t + 1 > (char*)s){\
t = _fmtflush(f, t, 1);\
if(t != nil)\
s = f->stop;\
else\
return -1;\
}\
*t++ = c;\
}while(0)
#define FMTRCHAR(f, t, s, c)\
do{\
if(t + 1 > (Rune*)s){\
t = _fmtflush(f, t, sizeof(Rune));\
if(t != nil)\
s = f->stop;\
else\
return -1;\
}\
*t++ = c;\
}while(0)
#define FMTRUNE(f, t, s, r)\
do{\
Rune _rune;\
int _runelen;\
if(t + UTFmax > (char*)s && t + (_runelen = jehanne_runelen(r)) > (char*)s){\
t = _fmtflush(f, t, _runelen);\
if(t != nil)\
s = f->stop;\
else\
return -1;\
}\
if(r < Runeself)\
*t++ = r;\
else{\
_rune = r;\
t += jehanne_runetochar(t, &_rune);\
}\
}while(0)

View File

@@ -0,0 +1,40 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/*
* public routine for final flush of a formatting buffer
* to a file descriptor; returns total char count.
*/
int
jehanne_fmtfdflush(Fmt *f)
{
if(_fmtFdFlush(f) <= 0)
return -1;
return f->nfmt;
}
/*
* initialize an output buffer for buffered printing
*/
int
jehanne_fmtfdinit(Fmt *f, int fd, char *buf, int size)
{
f->runes = 0;
f->start = buf;
f->to = buf;
f->stop = buf + size;
f->flush = _fmtFdFlush;
f->farg = (void*)(uintptr_t)fd;
f->nfmt = 0;
return 0;
}

View File

@@ -0,0 +1,25 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
static Lock fmtl;
void
_fmtlock(void)
{
jehanne_lock(&fmtl);
}
void
_fmtunlock(void)
{
jehanne_unlock(&fmtl);
}

View File

@@ -0,0 +1,30 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/*
* format a string into the output buffer
* designed for formats which themselves call fmt
*/
int
jehanne_fmtprint(Fmt *f, const char *fmt, ...)
{
va_list va;
int n;
va_start(va, fmt);
n = jehanne_fmtvprint(f, fmt, va);
va_end(va);
return n;
}

View File

@@ -0,0 +1,259 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/*
* How many bytes of output UTF will be produced by quoting (if necessary) this string?
* How many runes? How much of the input will be consumed?
* The parameter q is filled in by _quotesetup.
* The string may be UTF or Runes (s or r).
* Return count does not include NUL.
* Terminate the scan at the first of:
* NUL in input
* count exceeded in input
* count exceeded on output
* *ninp is set to number of input bytes accepted.
* nin may be <0 initially, to avoid checking input by count.
*/
void
_quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q,
int sharp, int runesout)
{
int w;
Rune c;
q->quoted = 0;
q->nbytesout = 0;
q->nrunesout = 0;
q->nbytesin = 0;
q->nrunesin = 0;
if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
if(nout < 2)
return;
q->quoted = 1;
q->nbytesout = 2;
q->nrunesout = 2;
}
for(; nin!=0; nin--){
if(s)
w = jehanne_chartorune(&c, s);
else{
c = *r;
w = jehanne_runelen(c);
}
if(c == '\0')
break;
if(runesout){
if(q->nrunesout+1 > nout)
break;
}else{
if(q->nbytesout+w > nout)
break;
}
if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
if(!q->quoted){
if(runesout){
if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
break;
}else{
if(1+q->nbytesout+w+1 > nout) /* no room for quotes */
break;
}
q->nrunesout += 2; /* include quotes */
q->nbytesout += 2; /* include quotes */
q->quoted = 1;
}
if(c == '\'') {
if(runesout){
if(1+q->nrunesout+1 > nout) /* no room for quotes */
break;
}else{
if(1+q->nbytesout+w > nout) /* no room for quotes */
break;
}
q->nbytesout++;
q->nrunesout++; /* quotes reproduce as two characters */
}
}
/* advance input */
if(s)
s += w;
else
r++;
q->nbytesin += w;
q->nrunesin++;
/* advance output */
q->nbytesout += w;
q->nrunesout++;
}
}
static int
qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
{
Rune r, *rm, *rme;
char *t, *s, *m, *me;
Rune *rt, *rs;
uint32_t fl;
int nc, w;
m = sin;
me = m + q->nbytesin;
rm = rin;
rme = rm + q->nrunesin;
w = f->width;
fl = f->flags;
if(f->runes){
if(!(fl & FmtLeft) && _rfmtpad(f, w - q->nrunesout) < 0)
return -1;
}else{
if(!(fl & FmtLeft) && _fmtpad(f, w - q->nbytesout) < 0)
return -1;
}
t = f->to;
s = f->stop;
rt = f->to;
rs = f->stop;
if(f->runes)
FMTRCHAR(f, rt, rs, '\'');
else
FMTRUNE(f, t, s, '\'');
for(nc = q->nrunesin; nc > 0; nc--){
if(sin){
r = *(uint8_t*)m;
if(r < Runeself)
m++;
else if((me - m) >= UTFmax || jehanne_fullrune(m, me-m))
m += jehanne_chartorune(&r, m);
else
break;
}else{
if(rm >= rme)
break;
r = *(uint8_t*)rm++;
}
if(f->runes){
FMTRCHAR(f, rt, rs, r);
if(r == '\'')
FMTRCHAR(f, rt, rs, r);
}else{
FMTRUNE(f, t, s, r);
if(r == '\'')
FMTRUNE(f, t, s, r);
}
}
if(f->runes){
FMTRCHAR(f, rt, rs, '\'');
USED(rs);
f->nfmt += rt - (Rune *)f->to;
f->to = rt;
if(fl & FmtLeft && _rfmtpad(f, w - q->nrunesout) < 0)
return -1;
}else{
FMTRUNE(f, t, s, '\'');
USED(s);
f->nfmt += t - (char *)f->to;
f->to = t;
if(fl & FmtLeft && _fmtpad(f, w - q->nbytesout) < 0)
return -1;
}
return 0;
}
int
_quotestrfmt(int runesin, Fmt *f)
{
int nin, outlen;
Rune *r;
char *s;
Quoteinfo q;
nin = -1;
if(f->flags&FmtPrec)
nin = f->prec;
if(runesin){
r = va_arg(f->args, Rune *);
s = nil;
}else{
s = va_arg(f->args, char *);
r = nil;
}
if(!s && !r)
return _fmtcpy(f, "<nil>", 5, 5);
if(f->flush)
outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
else if(f->runes)
outlen = (Rune*)f->stop - (Rune*)f->to;
else
outlen = (char*)f->stop - (char*)f->to;
_quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes);
//jehanne_print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
if(runesin){
if(!q.quoted)
return _fmtrcpy(f, r, q.nrunesin);
return qstrfmt(nil, r, &q, f);
}
if(!q.quoted)
return _fmtcpy(f, s, q.nrunesin, q.nbytesin);
return qstrfmt(s, nil, &q, f);
}
int
jehanne_quotestrfmt(Fmt *f)
{
return _quotestrfmt(0, f);
}
int
jehanne_quoterunestrfmt(Fmt *f)
{
return _quotestrfmt(1, f);
}
void
jehanne_quotefmtinstall(void)
{
jehanne_fmtinstall('q', jehanne_quotestrfmt);
jehanne_fmtinstall('Q', jehanne_quoterunestrfmt);
}
int
_needsquotes(char *s, int *quotelenp)
{
Quoteinfo q;
_quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
*quotelenp = q.nbytesout;
return q.quoted;
}
int
_runeneedsquotes(Rune *r, int *quotelenp)
{
Quoteinfo q;
_quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
*quotelenp = q.nrunesout;
return q.quoted;
}

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
int
jehanne_fmtrune(Fmt *f, int r)
{
Rune *rt;
char *t;
int n;
if(f->runes){
rt = f->to;
FMTRCHAR(f, rt, f->stop, r);
f->to = rt;
n = 1;
}else{
t = f->to;
FMTRUNE(f, t, f->stop, r);
n = t - (char*)f->to;
f->to = t;
}
f->nfmt += n;
return 0;
}

View File

@@ -0,0 +1,20 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
char*
jehanne_fmtstrflush(Fmt *f)
{
if(f->start == nil)
return nil;
*(char*)f->to = '\0';
return f->start;
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
/*
* format a string into the output buffer
* designed for formats which themselves call fmt
*/
int
jehanne_fmtvprint(Fmt *f, const char *fmt, va_list args)
{
va_list va;
int n;
//va = f->args;
//f->args = args;
va_copy(va,f->args);
va_end(f->args);
va_copy(f->args,args);
n = jehanne_dofmt(f, fmt);
va_end(f->args);
va_copy(f->args,va);
va_end(va);
//f->args = va;
if(n >= 0)
return 0;
return n;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_fprint(int fd, const char *fmt, ...)
{
int n;
va_list args;
va_start(args, fmt);
n = jehanne_vfprint(fd, fmt, args);
va_end(args);
return n;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_print(const char *fmt, ...)
{
int n;
va_list args;
va_start(args, fmt);
n = jehanne_vfprint(1, fmt, args);
va_end(args);
return n;
}

View File

@@ -0,0 +1,20 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
Rune*
jehanne_runefmtstrflush(Fmt *f)
{
if(f->start == nil)
return nil;
*(Rune*)f->to = '\0';
return f->start;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
Rune*
jehanne_runeseprint(Rune *buf, Rune *e, const char *fmt, ...)
{
Rune *p;
va_list args;
va_start(args, fmt);
p = jehanne_runevseprint(buf, e, fmt, args);
va_end(args);
return p;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
Rune*
jehanne_runesmprint(const char *fmt, ...)
{
va_list args;
Rune *p;
va_start(args, fmt);
p = jehanne_runevsmprint(fmt, args);
va_end(args);
return p;
}

View File

@@ -0,0 +1,24 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_runesnprint(Rune *buf, int len, const char *fmt, ...)
{
int n;
va_list args;
va_start(args, fmt);
n = jehanne_runevsnprint(buf, len, fmt, args);
va_end(args);
return n;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_runesprint(Rune *buf, const char *fmt, ...)
{
int n;
va_list args;
va_start(args, fmt);
n = jehanne_runevsnprint(buf, 256, fmt, args);
va_end(args);
return n;
}

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
Rune*
jehanne_runevseprint(Rune *buf, Rune *e, const char *fmt, va_list args)
{
Fmt f;
if(e <= buf)
return nil;
f.runes = 1;
f.start = buf;
f.to = buf;
f.stop = e - 1;
f.flush = nil;
f.farg = nil;
f.nfmt = 0;
//f.args = args;
va_copy(f.args,args);
jehanne_dofmt(&f, fmt);
va_end(f.args);
*(Rune*)f.to = '\0';
return f.to;
}

View File

@@ -0,0 +1,83 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
#include "fmtdef.h"
static int
runeFmtStrFlush(Fmt *f)
{
Rune *s;
int n;
if(f->start == nil)
return 0;
n = (int)(uintptr_t)f->farg;
n *= 2;
s = f->start;
f->start = jehanne_realloc(s, sizeof(Rune)*n);
if(f->start == nil){
f->farg = nil;
f->to = nil;
f->stop = nil;
jehanne_free(s);
return 0;
}
f->farg = (void*)(uintptr_t)n;
f->to = (Rune*)f->start + ((Rune*)f->to - s);
f->stop = (Rune*)f->start + n - 1;
return 1;
}
int
jehanne_runefmtstrinit(Fmt *f)
{
int n;
jehanne_memset(f, 0, sizeof *f);
f->runes = 1;
n = 32;
f->start = jehanne_malloc(sizeof(Rune)*n);
if(f->start == nil)
return -1;
jehanne_setmalloctag(f->start, jehanne_getcallerpc());
f->to = f->start;
f->stop = (Rune*)f->start + n - 1;
f->flush = runeFmtStrFlush;
f->farg = (void*)(uintptr_t)n;
f->nfmt = 0;
return 0;
}
/*
* print into an allocated string buffer
*/
Rune*
jehanne_runevsmprint(const char *fmt, va_list args)
{
Fmt f;
int n;
if(jehanne_runefmtstrinit(&f) < 0)
return nil;
//f.args = args;
va_copy(f.args,args);
n = jehanne_dofmt(&f, fmt);
va_end(f.args);
if(f.start == nil) /* realloc failed? */
return nil;
if(n < 0){
jehanne_free(f.start);
return nil;
}
jehanne_setmalloctag(f.start, jehanne_getcallerpc());
*(Rune*)f.to = '\0';
return f.start;
}

View File

@@ -0,0 +1,33 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_runevsnprint(Rune *buf, int len, const char *fmt, va_list args)
{
Fmt f;
if(len <= 0)
return -1;
f.runes = 1;
f.start = buf;
f.to = buf;
f.stop = buf + len - 1;
f.flush = nil;
f.farg = nil;
f.nfmt = 0;
//f.args = args;
va_copy(f.args,args);
jehanne_dofmt(&f, fmt);
va_end(f.args);
*(Rune*)f.to = '\0';
return (Rune*)f.to - buf;
}

View File

@@ -0,0 +1,23 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
char*
jehanne_seprint(char *buf, char *e, const char *fmt, ...)
{
char *p;
va_list args;
va_start(args, fmt);
p = jehanne_vseprint(buf, e, fmt, args);
va_end(args);
return p;
}

View File

@@ -0,0 +1,24 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
char*
jehanne_smprint(const char *fmt, ...)
{
va_list args;
char *p;
va_start(args, fmt);
p = jehanne_vsmprint(fmt, args);
va_end(args);
jehanne_setmalloctag(p, jehanne_getcallerpc());
return p;
}

View File

@@ -0,0 +1,24 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <libc.h>
int
jehanne_snprint(char *buf, int len, const char *fmt, ...)
{
int n;
va_list args;
va_start(args, fmt);
n = jehanne_vsnprint(buf, len, fmt, args);
va_end(args);
return n;
}

Some files were not shown because too many files have changed in this diff Show More