Luna/libc/src/pwd.cpp
apio e5a3bbcbbc
All checks were successful
continuous-integration/drone/push Build is passing
libc: Allow empty fields in the password file
2023-04-08 22:19:51 +02:00

140 lines
2.9 KiB
C++

#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct passwd pwd;
static FILE* f { nullptr };
static char buf[4096];
// Variant of strtok that returns empty tokens when there are several delimiters, instead of skipping all at once.
// Example:
// strtok("hello:world::!:", ":") -> "hello", "world", "!"
// _strtok_once("hello:world::!:", ":") -> "hello", "world", "", "!"
static char* _strtok_once(char* str, const char* delim)
{
static char* s = nullptr;
if (str) s = str;
if (!s) return nullptr;
if (*s)
{
if (*s == 0) return nullptr;
size_t use = strcspn(s, delim);
char* result = s;
if (s[use] != 0)
{
s[use] = 0;
s += (use + 1);
}
else { s = nullptr; }
return result;
}
return nullptr;
}
extern "C"
{
struct passwd* getpwent()
{
if (!f)
{
f = fopen("/etc/passwd", "r");
if (!f) return nullptr;
}
while (true)
{
char* rc = fgets(buf, sizeof(buf), f);
if (!rc) return nullptr;
char* name = _strtok_once(rc, ":\n");
if (!name) continue;
char* passwd = _strtok_once(nullptr, ":\n");
if (!passwd) continue;
char* uid = _strtok_once(nullptr, ":\n");
if (!uid) continue;
char* gid = _strtok_once(nullptr, ":\n");
if (!gid) continue;
char* gecos = _strtok_once(nullptr, ":\n");
if (!gecos) continue;
char* dir = _strtok_once(nullptr, ":\n");
if (!dir) continue;
char* shell = _strtok_once(nullptr, ":\n");
if (!shell) continue;
pwd.pw_name = name;
pwd.pw_passwd = passwd;
pwd.pw_uid = (uid_t)strtoul(uid, NULL, 10);
pwd.pw_gid = (gid_t)strtoul(gid, NULL, 10);
pwd.pw_gecos = gecos;
pwd.pw_dir = dir;
pwd.pw_shell = shell;
return &pwd;
}
}
struct passwd* getpwnam(const char* name)
{
setpwent();
struct passwd* entry;
while ((entry = getpwent()))
{
if (!strcmp(entry->pw_name, name))
{
endpwent();
return entry;
}
}
endpwent();
return entry;
}
struct passwd* getpwuid(uid_t uid)
{
setpwent();
struct passwd* entry;
while ((entry = getpwent()))
{
if (entry->pw_uid == uid)
{
endpwent();
return entry;
}
}
endpwent();
return entry;
}
void setpwent()
{
if (f) rewind(f);
}
void endpwent()
{
if (f)
{
fclose(f);
f = nullptr;
}
}
}