#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: devpath.c,v 1.2 2026/07/04 18:27:17 kre Exp $");
#endif
#include <sys/queue.h>
#include <err.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util.h>
#include "defs.h"
#include "devpath.h"
#include "devpath1.h"
#include "devpath2.h"
#include "devpath3.h"
#include "devpath4.h"
#include "devpath5.h"
#define easprintf (size_t)easprintf
typedef SIMPLEQ_HEAD(devpath_head, devpath_blk) devpath_head_t;
typedef struct devpath_blk {
devpath_elm_t path;
devpath_elm_t dbg;
SIMPLEQ_ENTRY(devpath_blk) entry;
} devpath_blk_t;
static void
devpath_end(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg)
{
assert(dp->Type == 0x7f);
assert(dp->Length == 4);
switch (dp->SubType) {
case 1:
path->cp = estrdup("");
path->sz = 1;
break;
case 0xff:
path->cp = NULL;
path->sz = 0;
break;
default:
path->sz = easprintf(&path->cp,
"unknown device path end subtype: %u\n", dp->SubType);
break;
}
if (dbg != NULL)
devpath_hdr(dp, dbg);
}
static char *
collapse_list(devpath_head_t *head, size_t plen, char **dmsg, size_t dlen)
{
devpath_blk_t *blk, *next;
char *bp, *path;
bp = path = emalloc(plen + 1);
SIMPLEQ_FOREACH_SAFE(blk, head, entry, next) {
if (blk->path.cp == NULL) {
if (next == NULL) {
*bp = '\0';
break;
}
continue;
}
else if (*blk->path.cp == '\0') {
*bp++ = ':';
next = SIMPLEQ_NEXT(blk, entry);
}
else {
bp = stpcpy(bp, blk->path.cp);
if (next->path.cp != NULL && *next->path.cp != '\0')
*bp++ = '/';
}
free(blk->path.cp);
}
if (dmsg) {
bp = *dmsg = emalloc(dlen + 1);
SIMPLEQ_FOREACH_SAFE(blk, head, entry, next) {
bp = stpcpy(bp, blk->dbg.cp);
free(blk->dbg.cp);
}
}
return path;
}
static void
devpath_parse_core(devpath_t *dp, devpath_elm_t *path, devpath_elm_t *dbg)
{
switch (dp->Type) {
case DEVPATH_TYPE_HW: devpath_hw(dp, path, dbg); return;
case DEVPATH_TYPE_ACPI: devpath_acpi(dp, path, dbg); return;
case DEVPATH_TYPE_MSG: devpath_msg(dp, path, dbg); return;
case DEVPATH_TYPE_MEDIA: devpath_media(dp, path, dbg); return;
case DEVPATH_TYPE_BIOS: devpath_bios(dp, path, dbg); return;
case DEVPATH_TYPE_END: devpath_end(dp, path, dbg); return;
default: devpath_unsupported(dp, path, dbg); return;
}
}
PUBLIC char *
devpath_parse(devpath_t *dp, size_t dplen, char **dmsg)
{
devpath_head_t head = SIMPLEQ_HEAD_INITIALIZER(head);
devpath_blk_t *blk;
union {
char *cp;
devpath_t *dp;
} u;
size_t dlen = 0, plen = 0;
char *ep;
if (dmsg)
*dmsg = NULL;
u.dp = dp;
ep = u.cp + dplen;
for (; u.cp < ep; u.cp += u.dp->Length) {
blk = ecalloc(1, sizeof(*blk));
devpath_parse_core(u.dp, &blk->path, dmsg ? &blk->dbg : NULL);
plen += blk->path.sz;
dlen += blk->dbg.sz;
SIMPLEQ_INSERT_TAIL(&head, blk, entry);
}
return collapse_list(&head, plen, dmsg, dlen);
}