| git.druid.rocks | index | druid520 | kaboom | src/ | user/ | string.nsc |
src/user/string.nsc
/*
* string.h coverage. lib.nsc used to also carry my_strlen/str_eq --
* real duplicates of strlen/strcmp here, kept around only because
* migrating every call site was never done. that migration has now
* happened: every program under src/user/ that needs a string length
* or comparison includes this file and calls strlen/strcmp/strncmp
* directly. str_eq had zero real callers anywhere and was deleted
* outright. my_strlen survives as a lib.nsc-internal helper only --
* stdio.nsc's fputs still calls it, since giving stdio.nsc its own
* string.nsh dependency would need a matching change to mk/bu.sh's
* per-program ".nsh"-grep dependency resolution (stdio.nsc is a
* shared library module, not a program bu.sh scans there), which is a
* build-system change, not a userland one.
*/
include "lib.nsh";
include "mem.nsh";
global u64
strlen(ptr s)
{
u64 i;
i = 0;
while(ptr_byte_at(s, i) != (u8)0)
{
i = i + (u64)1;
}
return i;
}
global ptr
strcpy(ptr dst, ptr src)
{
u64 n;
n = strlen(src);
memcpy(dst, src, n + (u64)1); /* +1 copies the null terminator too */
return dst;
}
/* real strncpy semantics, not "strcpy but capped": pads with nulls up
* to n if src is shorter, and does NOT null-terminate at all if src
* is n bytes or longer -- exactly as surprising as the real libc
* function, kept that way since anything porting real code expects
* this exact (mis)behavior. */
global ptr
strncpy(ptr dst, ptr src, u64 n)
{
u64 i;
u64 slen;
slen = strlen(src);
i = 0;
while(i < n)
{
if(i < slen)
{
mem_byte_set(dst, i, ptr_byte_at(src, i));
}
else
{
mem_byte_set(dst, i, (u8)0);
}
i = i + (u64)1;
}
return dst;
}
global ptr
strcat(ptr dst, ptr src)
{
u64 dlen;
u64 slen;
dlen = strlen(dst);
slen = strlen(src);
memcpy(dst + dlen, src, slen + (u64)1);
return dst;
}
global i32
strcmp(ptr a, ptr b)
{
u64 i;
u8 ca;
u8 cb;
i = 0;
while(1)
{
ca = ptr_byte_at(a, i);
cb = ptr_byte_at(b, i);
if(ca != cb)
{
if(ca < cb)
{
return -1;
}
return 1;
}
if(ca == (u8)0)
{
return 0;
}
i = i + (u64)1;
}
return 0;
}
global i32
strncmp(ptr a, ptr b, u64 n)
{
u64 i;
u8 ca;
u8 cb;
i = 0;
while(i < n)
{
ca = ptr_byte_at(a, i);
cb = ptr_byte_at(b, i);
if(ca != cb)
{
if(ca < cb)
{
return -1;
}
return 1;
}
if(ca == (u8)0)
{
return 0;
}
i = i + (u64)1;
}
return 0;
}
global ptr
strchr(ptr s, u8 ch)
{
u64 i;
u8 c;
i = 0;
while(1)
{
c = ptr_byte_at(s, i);
if(c == ch)
{
return s + i;
}
if(c == (u8)0)
{
return (ptr)0;
}
i = i + (u64)1;
}
return (ptr)0;
}
/* real strrchr semantics include ch==0 matching the terminator itself
* (i.e. strrchr(s, 0) returns a pointer to the end of the string) --
* scanning through the terminator's own position (i <= len, not <)
* gets that for free instead of needing a special case. */
global ptr
strrchr(ptr s, u8 ch)
{
u64 i;
u64 len;
ptr result;
len = strlen(s);
result = (ptr)0;
i = 0;
while(i <= len)
{
if(ptr_byte_at(s, i) == ch)
{
result = s + i;
}
i = i + (u64)1;
}
return result;
}