/* params.c -- Device parameter file handling. * * Copyright (C) 2016 Tomasz Kramkowski * * This program 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; either version 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include "halfkay.h" /*#include "log.h"*/ #include "eprintf.h" #include "params.h" #include "util.h" /* TODO: make this dynamic */ #define SZMAX_PFILE 1024 struct params { char *chip; char *board; struct flashparams fp; }; static int paramc = 0; static struct params *paramv = NULL; /* addparams: add a param definition to the param list */ static void addparams(struct params *p) { static int avail = 1; for (int i = 0; i < paramc; i++) { if (xstrcasecmp(paramv[i].chip, p->chip) != 0) continue; return; } if (paramc >= avail - 1) { avail *= 2; if ((unsigned)avail >= INT_MAX / 2 / sizeof *paramv) eprintf("Too many parameter files (%d)", paramc); paramv = erealloc(paramv, avail * sizeof *paramv); } paramv[paramc].chip = estrdup(p->chip); paramv[paramc].board = p->board != NULL ? estrdup(p->board) : NULL; paramv[paramc].fp = p->fp; paramc++; } static void freeparams(struct params *p) { free(p->chip); free(p->board); free(p); } /* atosz: convert string to size_t */ static int atosz(size_t *size, char *str) { unsigned long long val; char *endptr; assert(size); assert(str); /* for some silly reason strtoull actually allows negatives */ if (str[0] == '-') return -1; errno = 0; val = strtoull(str, &endptr, 0); if (errno || *endptr) return -1; if (val > SIZE_MAX) return -1; *size = val; return 0; } /* TODO: remove the need for freeparams by not allocating this */ /* readparams: read a file's param definition */ static struct params *readparams(const char *file) { const char *chip, *board; struct flashparams fp; struct params *par; size_t contsz; char cont[SZMAX_PFILE], *tok; FILE *f; assert(file); chip = file; for (size_t i = 0; i < strlen(file); i++) if (file[i] == '/') chip = &file[i + 1]; assert(chip[0]); f = fopen(file, "r"); if (f == NULL) { weprintf("Could not open '%s':", file); NULL; } contsz = fread(cont, 1, SZMAX_PFILE, f); if (contsz >= sizeof cont) { weprintf("Parameter file '%s' is longer than %zu", file, sizeof cont - 1); contsz = sizeof cont - 1; } for (size_t i = 0; i < contsz; i++) { if (cont[i] == '\0') { weprintf("Parameter file '%s' contains null byte", file); cont[i] = ' '; } else if (isspace(cont[i]) || !isprint(cont[i])) { cont[i] = ' '; } } cont[contsz] = '\0'; tok = strtok(cont, " "); if (tok == NULL) goto fail; if (atosz(&fp.memsz, tok) != 0) goto fail; tok = strtok(NULL, " "); if (tok == NULL) goto fail; if (atosz(&fp.blksz, tok) != 0) goto fail; tok = strtok(NULL, " "); if (tok == NULL) goto fail; if (atosz(&fp.cmdsz, tok) != 0) goto fail; tok = strtok(NULL, " "); if (tok == NULL) goto fail; if (atosz(&fp.addrshft, tok) != 0) goto fail; board = strtok(NULL, ""); par = emalloc(sizeof *par); par->chip = estrdup(chip); par->board = board != NULL ? estrdup(board) : NULL; par->fp = fp; fail: if (par == NULL) weprintf("Parameter file '%s' is malformed", file); fclose(f); return par; } /* getdirs: returns a NULL terminated list of data paths */ static char **getdirs(void) { static const char *globsuffix = "/hktool/*"; char **dirs, *xddirs, *tok; int len = 0, avail = 1; xddirs = getenv("XDG_DATA_DIRS"); if (!xddirs || !*xddirs) xddirs = "/usr/local/share:/usr/share"; xddirs = estrdup(xddirs); dirs = emalloc(sizeof *dirs); for (char *s = xddirs; tok = strtok(s, ":"), tok != NULL; s = NULL) { size_t slen; if (tok[0] == '\\') continue; if (len >= avail - 1) { avail *= 2; if ((unsigned)avail >= INT_MAX / 2 / sizeof *dirs) eprintf("Too many data dirs (%d)", len); dirs = erealloc(dirs, avail * sizeof *dirs); } if (SIZE_MAX - (strlen(tok) + 1) < strlen(globsuffix)) eprintf("Data dir too long (\"%.20s\")", tok); slen = strlen(tok) + strlen(globsuffix) + 1; dirs[len] = emalloc(slen); snprintf(dirs[len], slen, "%s%s", tok, globsuffix); len++; } free(xddirs); dirs[len++] = NULL; dirs = erealloc(dirs, len * sizeof *dirs); return dirs; } /* freedirs: frees list returned by getdirs */ static void freedirs(char **dirs) { assert(dirs); for (size_t i = 0; dirs[i]; i++) free(dirs[i]); free(dirs); } /* loadparams: locate and load all param files */ static void loadparams(void) { char **dirs; glob_t globbuf; if (paramv != NULL) return; dirs = getdirs(); for (int i = 0; dirs[i]; i++) { int ret; ret = glob(dirs[i], i != 0 ? GLOB_APPEND : 0, NULL, &globbuf); if (ret == GLOB_NOSPACE) eprintf("glob ran out of memory"); if (ret == GLOB_NOMATCH) continue; if (ret == GLOB_ABORTED) { weprintf("A call to glob resulted in a read error"); continue; } } freedirs(dirs); for (size_t i = 0; i < globbuf.gl_pathc; i++) { struct params *par; par = readparams(globbuf.gl_pathv[i]); if (par == NULL) continue; addparams(par); freeparams(par); } globfree(&globbuf); } static int b16len(size_t n) { return snprintf(NULL, 0, "%zx", n); } /* listparams: display a list of found parameter files */ void listparams(void) { int lengths[5] = { 0 }; loadparams(); for (int i = 0; i < paramc; i++) { int clengths[5]; clengths[0] = strlen(paramv[i].chip); clengths[1] = b16len(paramv[i].fp.memsz); clengths[2] = b16len(paramv[i].fp.blksz); clengths[3] = b16len(paramv[i].fp.cmdsz); clengths[4] = b16len(paramv[i].fp.addrshft); for (int j = 0; j < 5; j++) if (clengths[j] > lengths[j]) lengths[j] = clengths[j]; } for (int i = 0; i < paramc; i++) printf("%-*s - (0x%.*zx 0x%.*zx 0x%.*zx 0x%.*zx) %s\n", lengths[0], paramv[i].chip, lengths[1], paramv[i].fp.memsz, lengths[2], paramv[i].fp.blksz, lengths[3], paramv[i].fp.cmdsz, lengths[4], paramv[i].fp.addrshft, paramv[i].board != NULL ? paramv[i].board : ""); } /* getparams: return the flash params for the specified MCU */ int getparams(struct flashparams *fp, const char *mcufile) { assert(fp); assert(mcufile); loadparams(); if (strchr(mcufile, '/') != NULL) { struct params *par; par = readparams(mcufile); if (par == NULL) return -1; *fp = par->fp; freeparams(par); return 0; } for (int i = 0; i < paramc; i++) { if (xstrcasecmp(paramv[i].chip, mcufile) == 0) { *fp = paramv[i].fp; return 0; } } weprintf("'%s' is not a valid MCU name", mcufile); return -1; }