/* bt_dev.c — 蓝牙 A2DP 设备自识别(纯逻辑实现,见 bt_dev.h 契约)。 * * 纯 libc(string / stdlib),无任何 meow_* 调用、无 popen、无文件 I/O —— meow_audiod 负责 * 读取 /usr/data/alsa.conf、执行 `bluealsa-cli list-pcms` 及写回。宿主单测零链路重。 */ #include "bt_dev.h" #include #include #include /* 解析 bluealsa-cli list-pcms 输出,取第一个 a2dpsrc/a2dp_source 端点的设备 MAC。 * 端点形如 `a2dpsrc/AA:BB:CC:DD:EE:FF`(MAC 在端点 token 内最后一个 '/' 之后到空白前)。 * 逐个**有界行**匹配(strstr 必须在行内,避免跨换行误把后一行当本行端点——如行 1 * "profile/aptx" 与行 3 "a2dp_source/..." 的经典误判)。 */ int bt_dev_detect_mac(const char *listpcms, char *mac, size_t macsz) { if (!listpcms || !mac || macsz < 2) return -1; mac[0] = 0; const char *ln = listpcms; while (ln && *ln) { const char *nl = strchr(ln, '\n'); const char *le = nl ? nl : (ln + strlen(ln)); size_t linelen = (size_t)(le - ln); char line[256]; if (linelen >= sizeof line) linelen = sizeof line - 1; memcpy(line, ln, linelen); line[linelen] = 0; const char *p = line; while (*p == ' ' || *p == '\t') p++; const char *hit = strstr(p, "a2dpsrc"); if (!hit) hit = strstr(p, "a2dp_source"); if (hit) { const char *te = hit; while (*te && *te != ' ' && *te != '\t' && *te != '\n') te++; const char *slash = NULL; for (const char *q = hit; q < te; q++) if (*q == '/') slash = q; if (slash && (slash + 1) < te) { const char *start = slash + 1; size_t len = (size_t)(te - start); if (len > 0 && len < macsz) { memcpy(mac, start, len); mac[len] = 0; return 0; } } } if (!nl) break; ln = nl + 1; } return -1; } /* 在单行内把 anchor("DEV=" / "device")后的 MAC 值替换为 mac,保留引号风格。 * 行未含 anchor → 返回 1(out=原样);含 anchor → 写回新行并返回 0。 */ static int line_replace_mac(char *out, size_t outsz, const char *line, const char *anchor, const char *mac) { const char *at = strstr(line, anchor); if (!at) { snprintf(out, outsz, "%s", line); return 1; } const char *vp = at + strlen(anchor); while (*vp == ' ' || *vp == '\t') vp++; if (*vp == '"') vp++; /* 引号风格:值可带双引号 */ const char *ve = vp; while (*ve && *ve != ' ' && *ve != '\t' && *ve != '\n' && *ve != '"' && *ve != '{' && *ve != '}') ve++; size_t prefix = (size_t)(vp - line); /* 前段(含开引号,若带) */ size_t tail = (size_t)(ve - line); /* 值终点(不含闭引号,若带) */ /* 前段 + mac + 尾段(尾段保留闭引号与其后内容) */ snprintf(out, outsz, "%.*s%s%s", (int)prefix, line, mac, line + tail); return 0; } /* 幂等改写 alsa.conf:只把 `pcm.bluealsa` 块内 `DEV=` 与 `device` 后的 MAC 换成 mac。 * 逐行处理;无 pcm.bluealsa / 无 DEV= / device token → -1;两端已一致 → 1;改了 → 0。 */ int bt_dev_rewrite(const char *in, size_t in_len, const char *mac, char **out, size_t *out_len) { if (!in || !mac || !mac[0] || !out || !out_len || in_len == 0) return -1; if (!strstr(in, "pcm.bluealsa") || (!strstr(in, "DEV=") && !strstr(in, "device"))) return -1; size_t cap = in_len + 128; char *res = malloc(cap ? cap : 1); if (!res) return -1; size_t o = 0, changed = 0; const char *p = in, *end = in + in_len; while (p < end) { const char *nl = memchr(p, '\n', (size_t)(end - p)); const char *le = nl ? nl : end; size_t linelen = (size_t)(le - p); char line[512], newline[512]; if (linelen >= sizeof line) linelen = sizeof line - 1; memcpy(line, p, linelen); line[linelen] = 0; const char *k = line; while (*k == ' ' || *k == '\t') k++; int anchored = 0; if (strstr(k, "DEV=")) { line_replace_mac(newline, sizeof newline, line, "DEV=", mac); anchored = 1; } else if (strncmp(k, "device", 6) == 0 && (k[6] == ' ' || k[6] == '\t' || k[6] == 0)) { line_replace_mac(newline, sizeof newline, line, "device", mac); anchored = 1; } if (anchored) { if (strcmp(line, newline) != 0) changed = 1; size_t n = strlen(newline); if (o + n + 2 > cap) { cap = o + n + 256; char *t = realloc(res, cap); if (!t) { free(res); return -1; } res = t; } memcpy(res + o, newline, n); o += n; if (nl) res[o++] = '\n'; } else { size_t n = linelen; if (o + n + 2 > cap) { cap = o + n + 256; char *t = realloc(res, cap); if (!t) { free(res); return -1; } res = t; } memcpy(res + o, p, n); o += n; if (nl) res[o++] = '\n'; } if (!nl) break; p = nl + 1; } if (!changed) { free(res); return 1; } res[o] = 0; *out = res; *out_len = o; return 0; }