/* $Id$ */ /* Copyright (c) 2011-2019 Pierre Pronchery */ /* Copyright (c) 2012 Baptiste Daroussin */ /* This file is part of DeforaOS Devel configure */ /* All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "../config.h" #ifndef __unused # define __unused #endif #ifndef MAXPATHLEN # define MAXPATHLEN 1024 /* XXX */ #endif #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef PROGNAME # define PROGNAME "pkg-config" #endif #define PKG_CFLAGS (1 << 0) #define PKG_CFLAGS_ONLY_I (1 << 1) #define PKG_CFLAGS_ONLY_OTHERS (1 << 2) #define PKG_LIBS (1 << 3) #define PKG_LIBS_ONLY_L (1 << 4) #define PKG_LIBS_ONLY_l (1 << 5) #define PKG_LIBS_ONLY_OTHERS (1 << 6) #define PKG_DESCRIPTION (1 << 7) #define PKG_URL (1 << 8) #define PKG_STATIC (1 << 9) #define PKG_VERSION (1 << 10) #define PKG_EXISTS (1 << 11) #define PKG_MODVERSION (1 << 12) #define PKG_PRINT_REQUIRES (1 << 13) #define PKG_PRINT_REQUIRES_PRIV (1 << 14) /* pkg-config without glib without pkg-config without glib without pkg-config */ /* private */ /* types */ typedef struct _ListData { void *data; void (*freefn)(void *free); } ListData; typedef struct _PkgList { size_t cap; size_t len; ListData **data; } PkgList; typedef struct _PkgConfigVariable { char * name; char * value; } PkgConfigVariable; typedef enum { GT, LT, GE, LE, EQ } operator; typedef struct _Pkg { char *pkgname; char *name; char *version; char *description; char *url; PkgList *cflags; PkgList *cflags_private; PkgList *libs; PkgList *libs_private; PkgList *requires; PkgList *requires_private; PkgList *variables; } Pkg; typedef struct _PkgRequires { char *name; char *version; operator op; Pkg *pkg; } PkgRequires; typedef struct _PkgConfig { /* variables */ unsigned int flags; PkgList *pc_dirs; PkgList *pkgs; } PkgConfig; /* prototypes */ static int _pkgconfig(PkgConfig * pc, int pkgc, char * pkgv[]); static int _pkgconfig_error(int ret, char const * format, ...); /* lists */ static PkgList * _pkglist_new(PkgList **list); static void _pkglist_delete(PkgList *list); static void * _pkglist_get(PkgList *list, size_t i); static void _pkglist_append(PkgList *list, void *data, void (*freefn)(void *)); /* string */ static int _string_append(char ** string, char const * append); static int _string_append_length(char ** string, char const * append, size_t length); static int _usage(int brief); /* functions */ /* pkgconfig */ static Pkg * _pkg_new(Pkg **pkg, char const * pkgname); static void _pkgconfig_variable_delete(PkgConfigVariable *p); static FILE * _pkgconfig_open(PkgConfig * pc, char const * pkg); static int _pkgconfig_parse(PkgConfig * p, FILE * fp); static int _pkgconfig_parse_directive(PkgConfig * pc, Pkg * p, char const * directive, char const * value); static char * _pkgconfig_parse_substitute(PkgList * v, char const * value); static int _pkgconfig_parse_variable(PkgConfig * pc, Pkg * p, char const * name, char const * value); static int _pkgconfig_parse_name(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_description(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_version(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_cflags(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_cflags_private(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_libs(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_libs_private(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_requires(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_requires_private(PkgConfig *pc, Pkg *p, char *data); static int _pkgconfig_parse_requires_generic(PkgConfig *pc, Pkg *p, char *data, PkgList *lst); struct keys { const char *key; int (*parse)(PkgConfig *, Pkg *, char *); } keys [] = { { "Name", _pkgconfig_parse_name }, { "Description", _pkgconfig_parse_description }, { "Version", _pkgconfig_parse_version }, { "Cflags", _pkgconfig_parse_cflags }, { "Cflags.private", _pkgconfig_parse_cflags_private }, { "Libs", _pkgconfig_parse_libs }, { "Libs.private", _pkgconfig_parse_libs_private }, { "Requires", _pkgconfig_parse_requires }, { "Requires.private", _pkgconfig_parse_requires_private }, { NULL, NULL }, }; static PkgRequires * _pkgrequires_new(PkgRequires **p, char *name) { if (*p != NULL) return *p; if ((*p = malloc(sizeof(PkgRequires))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return NULL; } (*p)->name = strdup(name); (*p)->op = EQ; (*p)->version = NULL; (*p)->pkg = NULL; return *p; } static void _pkgrequires_delete(void *p1) { PkgRequires *p = (PkgRequires *)p1; if (p == NULL) return; if (p->name != NULL) free(p->name); if (p->version != NULL) free(p->version); } static Pkg * _pkg_new(Pkg **pkg, char const * pkgname) { if (*pkg != NULL) return *pkg; if ((*pkg = malloc(sizeof(Pkg))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return NULL; } (*pkg)->name = NULL; (*pkg)->version = NULL; (*pkg)->description = NULL; (*pkg)->url = NULL; (*pkg)->pkgname = strdup(pkgname); (*pkg)->cflags = NULL; (*pkg)->cflags_private = NULL; (*pkg)->libs = NULL; (*pkg)->libs_private = NULL; (*pkg)->variables = NULL; (*pkg)->requires = NULL; (*pkg)->requires_private = NULL; return *pkg; } static void _pkg_delete(void *pkg) { Pkg *p = (Pkg *)pkg; if (p->pkgname != NULL) free(p->pkgname); if (p->name != NULL) free(p->name); if (p->version != NULL) free(p->version); if (p->description != NULL) free(p->description); if (p->url != NULL) free(p->url); _pkglist_delete(p->cflags); _pkglist_delete(p->cflags_private); _pkglist_delete(p->libs); _pkglist_delete(p->libs_private); _pkglist_delete(p->variables); _pkglist_delete(p->requires); _pkglist_delete(p->requires_private); } static PkgConfigVariable *_pkgconfig_variable_new(PkgConfigVariable **p) { if (*p != NULL) return *p; if ((*p = malloc(sizeof(PkgConfigVariable))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return NULL; } (*p)->name = NULL; (*p)->value = NULL; return *p; } static void _pkgconfig_variable_delete(PkgConfigVariable *p) { free(p->name); free(p->value); free(p); } static int _pkgconfig_parse_name(__unused PkgConfig *pc, Pkg *p, char *data) { int ret = 0; if (p->name != NULL) free(p->name); p->name = strdup(data); return ret; } static int _pkgconfig_parse_description(PkgConfig *pc, Pkg *p, char *data) { int ret = 0; if ((pc->flags & PKG_DESCRIPTION) == 0) return ret; if (p->description != NULL) free(p->description); p->description = strdup(data); return ret; } static int _pkgconfig_parse_version(PkgConfig *pc, Pkg *p, char *data) { int ret = 0; if ((pc->flags & PKG_MODVERSION) == 0) return ret; if (p->version != NULL) free(p->version); p->version = strdup(data); return ret; } static int split_chr(char *str, char sep) { char *next; char *buf = str; int nbel = 0; while ((next = strchr(buf, sep)) != NULL) { nbel++; buf = next; buf[0] = '\0'; buf++; } return nbel; } static void _pkgconfig_add_to_list(PkgList *lst, char *data) { size_t k; /* do not append something already appended */ for (k = 0; k < lst->len; k++) if (strcmp((char *)_pkglist_get(lst, k), data) == 0) break; if (k == lst->len) _pkglist_append(lst, strdup(data), free); } static int _pkgconfig_parse_generic(PkgList *lst, char *data, unsigned int flags, unsigned int type) { int ret = 0; int i; int nbel = 0; size_t next; char *walk; nbel = split_chr(data, ' '); next = strlen(data); walk = data; for (i = 0; i <= nbel; i++) { if (next != 0) { if (type == PKG_CFLAGS) { if (flags & PKG_CFLAGS_ONLY_I) { if (strncmp(walk, "-I", 2) == 0) _pkgconfig_add_to_list(lst, walk); } else if ((flags & PKG_CFLAGS_ONLY_OTHERS) == PKG_CFLAGS_ONLY_OTHERS) { if (strncmp(walk, "-I", 2) != 0) _pkgconfig_add_to_list(lst, walk); } else { _pkgconfig_add_to_list(lst, walk); } } if (type == PKG_LIBS) { if (flags & PKG_LIBS_ONLY_l) { if (strncmp(walk, "-l", 2) == 0) _pkgconfig_add_to_list(lst, walk); } else if (flags & PKG_LIBS_ONLY_L) { if (strncmp(walk, "-L", 2) == 0) _pkgconfig_add_to_list(lst, walk); } else if (flags & PKG_LIBS_ONLY_OTHERS) { if (strncmp(walk, "-L", 2) != 0 && strncmp(walk, "-l", 2) != 0) _pkgconfig_add_to_list(lst, walk); } else { _pkgconfig_add_to_list(lst, walk); } } } if (i != nbel) { walk += next + 1; next = strlen(walk); } } return ret; } static int _pkgconfig_parse_cflags(PkgConfig *pc, Pkg *p, char *data) { if ((pc->flags & (PKG_CFLAGS|PKG_CFLAGS_ONLY_I|PKG_CFLAGS_ONLY_OTHERS)) == 0) return 0; _pkglist_new(&p->cflags); return _pkgconfig_parse_generic(p->cflags, data, pc->flags, PKG_CFLAGS); } static int _pkgconfig_parse_cflags_private(PkgConfig *pc, Pkg *p, char *data) { if ((pc->flags & (PKG_CFLAGS|PKG_CFLAGS_ONLY_I|PKG_CFLAGS_ONLY_OTHERS)) == 0) return 0; _pkglist_new(&p->cflags_private); return _pkgconfig_parse_generic(p->cflags_private, data, pc->flags, PKG_CFLAGS); } static int _pkgconfig_parse_libs(PkgConfig *pc, Pkg *p, char *data) { if ((pc->flags & (PKG_LIBS|PKG_LIBS_ONLY_L|PKG_LIBS_ONLY_l|PKG_LIBS_ONLY_OTHERS)) == 0) return 0; _pkglist_new(&p->libs); return _pkgconfig_parse_generic(p->libs, data, pc->flags, PKG_LIBS); } static int _pkgconfig_parse_libs_private(PkgConfig *pc, Pkg *p, char *data) { if ((pc->flags & (PKG_LIBS|PKG_LIBS_ONLY_L|PKG_LIBS_ONLY_l|PKG_LIBS_ONLY_OTHERS)) == 0) return 0; _pkglist_new(&p->libs_private); return _pkgconfig_parse_generic(p->libs_private, data, pc->flags, PKG_LIBS); } static int _pkgconfig_parse_requires_private(PkgConfig *pc ,Pkg *p, char *data) { if (pc->flags & PKG_MODVERSION) return 0; _pkglist_new(&p->requires_private); return _pkgconfig_parse_requires_generic(pc, p, data, p->requires_private); } static int _pkgconfig_parse_requires(PkgConfig *pc, Pkg *p, char *data) { if (pc->flags & PKG_MODVERSION) return 0; _pkglist_new(&p->requires); return _pkgconfig_parse_requires_generic(pc, p, data, p->requires); } static void * _pkglist_lookup(PkgList *list, void *data, int (*compar)(const char *, const char *)) { size_t i; for (i = 0; i < list->len; i++) if (compar(_pkglist_get(list, i), data) == 0) return _pkglist_get(list, i); return NULL; } static void printout(PkgList *list, char * toprint) { if (_pkglist_lookup(list, toprint, strcmp) != NULL) return; printf("%s ", toprint); _pkglist_append(list, toprint, NULL); } static int _pkgconfig(PkgConfig * pc, int pkgc, char * pkgv[]) { int ret = 0; PkgList *printed = NULL; char out = '\0'; char const format[] = "Package %s was not found in the " PROGNAME " search path.\n" "Perhaps you should add the directory containing `%s.pc'\n" "to the PKG_CONFIG_PATH environment variable\n" "No package '%s' found\n"; char const * libdir, * libpath; int i; size_t j, k; FILE * fp; Pkg *p; /* default values */ _pkglist_append(pc->pc_dirs, "/usr/lib/pkgconfig", NULL); _pkglist_append(pc->pc_dirs, "/usr/libdata/pkgconfig", NULL); _pkglist_append(pc->pc_dirs, PREFIX"/libdata/pkgconfig", NULL); _pkglist_append(pc->pc_dirs, PREFIX"/lib/pkgconfig", NULL); _pkglist_append(pc->pc_dirs, PREFIX"/share/pkgconfig", NULL); /* environment variables */ libdir = getenv("PKG_CONFIG_LIBDIR"); libpath = getenv("PKG_CONFIG_PATH"); _pkglist_new(&printed); /* TODO parse libpath and libdir */ if (libpath != NULL) { _pkglist_delete(pc->pc_dirs); } else if (libdir != NULL) { _pkglist_delete(pc->pc_dirs); } /* packages */ for(i = 0; i < pkgc; i++) { /* Do not try to load already loaded pkgs */ for (j = 0; j < pc->pkgs->len; j++) if (strcmp(((Pkg *) _pkglist_get(pc->pkgs, j))->pkgname, pkgv[i]) == 0) break; if (j < pc->pkgs->len) continue; if ((fp = _pkgconfig_open(pc, pkgv[i])) != NULL) { if ((pc->flags & PKG_EXISTS) == 0) ret |= _pkgconfig_parse(pc, fp); fclose(fp); } else { if ((pc->flags & PKG_EXISTS) == 0) return _pkgconfig_error(1, format, pkgv[i], pkgv[i], pkgv[i]); else return 1; } } if (pc->flags & PKG_MODVERSION) { for (j = 0; j < pc->pkgs->len; j++) { p = (Pkg*)_pkglist_get(pc->pkgs, j); printf("%s\n",p->version); } _pkglist_delete(printed); return 0; } if (pc->flags & (PKG_PRINT_REQUIRES|PKG_PRINT_REQUIRES_PRIV)) { for (j = 0; j < pc->pkgs->len; j++) { p = (Pkg *)_pkglist_get(pc->pkgs, j); if (pc->flags & PKG_PRINT_REQUIRES && p->requires != NULL) for (k = 0; k < p->requires->len; k++) printf("%s\n", ((PkgRequires *)_pkglist_get(p->requires, k))->name); if (pc->flags & PKG_PRINT_REQUIRES_PRIV && p->requires_private != NULL) for (k = 0; k < p->requires_private->len; k++) printf("%s\n", ((PkgRequires *)_pkglist_get(p->requires_private, k))->name); } _pkglist_delete(printed); return 0; } if (pc->flags & (PKG_CFLAGS|PKG_CFLAGS_ONLY_OTHERS|PKG_CFLAGS_ONLY_I)) { for (j = 0; j < pc->pkgs->len; j++) { p = (Pkg*)_pkglist_get(pc->pkgs, j); if (p->cflags == NULL) continue; for (k = 0; k < p->cflags->len; k++) { printout(printed, (char *)_pkglist_get(p->cflags, k)); out='\n'; } } } if (pc->flags & (PKG_LIBS|PKG_LIBS_ONLY_L|PKG_LIBS_ONLY_l|PKG_LIBS_ONLY_OTHERS)) { for (j = 0; j < pc->pkgs->len; j++) { p = (Pkg *)_pkglist_get(pc->pkgs, j); if (p->libs == NULL) continue; for (k = 0; k < p->libs->len; k++) { printout(printed, (char *)_pkglist_get(p->libs, k)); out='\n'; } if (pc->flags & PKG_STATIC && p->libs_private != NULL) { for (k = 0; k < p->libs_private->len; k++) { printout(printed, (char *)_pkglist_get(p->libs_private, k)); out='\n'; } } } } printf("%c", out); _pkglist_delete(printed); return ret; } static FILE * _pkgconfig_open(PkgConfig *pc, char const * pkg) { FILE * fp; size_t i; char path[MAXPATHLEN]; Pkg *p = NULL; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, pkg); #endif for (i = 0; i < pc->pc_dirs->len; i++) { snprintf(path, sizeof(path), "%s/%s.pc", (char *)_pkglist_get(pc->pc_dirs, i), pkg); if ((fp = fopen(path, "r")) != NULL) { p = _pkg_new(&p, pkg); _pkglist_append(pc->pkgs, p, _pkg_delete); return fp; } } return NULL; } static int _pkgconfig_parse(PkgConfig * pc, FILE * fp) { Pkg *p; char * line; const size_t len = 256; size_t i; p = _pkglist_get(pc->pkgs, pc->pkgs->len - 1); if((line = malloc(len)) == NULL) return -_pkgconfig_error(1, "%s", strerror(errno)); while(fgets(line, len, fp) != NULL) { i = strlen(line); if(line[i - 1] != '\n') return -_pkgconfig_error(1, "%s: %s", p->pkgname, "Line too long"); line[i - 1] = '\0'; /* detect empty lines or comments */ for(i = 0; line[i] != '\0' && isspace((unsigned char)line[i]); i++); if(line[i] == '\0' || line[i] == '#') continue; /* look for a '=' or a ':' in the line */ for(i = 0; line[i] != '\0' && (isalnum((unsigned char)line[i]) || line[i] == '_' || line[i] == '.'); i++); if(line[i] == '=') { line[i] = '\0'; for(i += 1; line[i] != '\0' && isspace((unsigned char)line[i]); i++); if (line[i] == '\0') continue; if(_pkgconfig_parse_variable(pc, p, line, &line[i]) != 0) return -1; } else if(line[i] == ':') { line[i] = '\0'; for(i += 1; line[i] != '\0' && isspace((unsigned char)line[i]); i++); if (line[i] == '\0') continue; if(_pkgconfig_parse_directive(pc, p, line, &line[i]) != 0) return -1; } #ifdef DEBUG else fprintf(stderr, "DEBUG: %s\n", line); #endif } free(line); return 0; } static int _pkgconfig_parse_directive(PkgConfig * pc, Pkg *pkg, char const * directive, char const * value) { int ret = 0; char * p; int i = 0; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\")\n", __func__, directive, value); #endif if((p = _pkgconfig_parse_substitute(pkg->variables, value)) == NULL) return -1; for (i = 0; keys[i].key != NULL; i++) { if (strcmp(directive, keys[i].key) == 0) { keys[i].parse(pc, pkg, p); } } free(p); return ret; } static int _pkgconfig_parse_requires_generic(PkgConfig *pc, Pkg * pkg, char * requires, PkgList *lst) { int ret = 0; int i; size_t j; char * p; Pkg *reqp; FILE * fp; int expect_version = 0; operator op; int nbel; char *walk; size_t next; PkgRequires *req = NULL; if (pc->flags & PKG_MODVERSION) return 0; if (pc->flags & (PKG_LIBS|PKG_LIBS_ONLY_L|PKG_LIBS_ONLY_l|PKG_LIBS_ONLY_L) && (pc->flags & PKG_STATIC) == 0) return 0; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, requires); #endif if((p = _pkgconfig_parse_substitute(pkg->variables, requires)) == NULL) return -1; nbel = split_chr(p, ' '); nbel += split_chr(p, '\t'); nbel += split_chr(p, ','); next = strlen(p); walk = p; for (i = 0; i <= nbel; i++) { if (next == 0) { walk += next + 1; next = strlen(walk); continue; } if (walk[0] == '>') { if (req == NULL) return -_pkgconfig_error(1, "malformed entry"); op = GT; if (walk[0] == '=') op = GE; expect_version = 1; } else if (walk[0] == '<') { if (req == NULL) return -_pkgconfig_error(1, "malformed entry"); op = LT; if (walk[0] == '=') op = LE; expect_version = 1; } else if (walk[0] == '=') { if (req == NULL) return -_pkgconfig_error(1, "malformed entry"); op = EQ; expect_version = 1; } else { if (!expect_version) { req = NULL; _pkgrequires_new(&req, walk); for (j = 0; j < pc->pkgs->len; j++) { reqp = (Pkg*)_pkglist_get(pc->pkgs, j); if (strcmp(reqp->pkgname, walk) == 0) { req->pkg = reqp; break; } } _pkglist_append(lst, req, _pkgrequires_delete); if (req->pkg == NULL) { if ((fp = _pkgconfig_open(pc, req->name)) != NULL) { if ((pc->flags & (PKG_PRINT_REQUIRES|PKG_PRINT_REQUIRES_PRIV)) == 0) { ret |= _pkgconfig_parse(pc, fp); reqp = (Pkg *)_pkglist_get(pc->pkgs, pc->pkgs->len -1); req->pkg = reqp; } fclose(fp); } else { ret |= _pkgconfig_error(1, "%s: %s", req->name, strerror(errno)); } } } else { req->version = strdup(walk); expect_version = 0; } } walk += next + 1; next = strlen(walk); } free(p); return ret; } static char * _pkgconfig_parse_substitute(PkgList * vars, char const * value) { char * ret = NULL; PkgConfigVariable *var = NULL; size_t i; size_t j; size_t k; for(i = 0; value[i] != '\0'; i++) { if(value[i] != '$' || value[i + 1] != '{') { /* XXX not efficient */ if(_string_append_length(&ret, &value[i], 1) != 0) { free(ret); return NULL; } continue; } for(j = i + 2; value[j] != '\0' && value[j] != '}'; j++); if(value[j] != '}') { free(ret); return NULL; } for(k = 0; k < vars->len; k++) { var = (PkgConfigVariable *)_pkglist_get(vars, k); if(strncmp(var->name, &value[i + 2], j - i - 2) == 0) break; var = NULL; } if(var == NULL) { /* FIXME report error */ free(ret); return NULL; } if(_string_append(&ret, var->value) != 0) { free(ret); return NULL; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s => %s\n", var->name, var->value); #endif i = j; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s() => \"%s\"\n", __func__, ret); #endif return ret; } static int _pkgconfig_parse_variable(__unused PkgConfig * pc, Pkg *pkg, char const * name, char const * value) { PkgConfigVariable * p = NULL; char * q; size_t i; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\")\n", __func__, name, value); #endif if((q = _pkgconfig_parse_substitute(pkg->variables, value)) == NULL) return -1; _pkglist_new(&pkg->variables); /* check if the variable already exists */ for(i = 0; i < pkg->variables->len; i++) { p = (PkgConfigVariable *)_pkglist_get(pkg->variables, i); if(strcmp(p->name, name) == 0) { free(p->value); p->value = q; return 0; } } /* allocate a new variable */ p = NULL; p = _pkgconfig_variable_new(&p); if((p->name = strdup(name)) == NULL) { _pkgconfig_variable_delete(p); return -_pkgconfig_error(1, "%s", strerror(errno)); } p->value = q; /* TODO cleanup function */ _pkglist_append(pkg->variables, p, NULL); return 0; } /* pkgconfig_error */ static int _pkgconfig_error(int ret, char const * format, ...) { va_list ap; fputs(PROGNAME ": ", stderr); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); return ret; } /* string_append */ static int _string_append(char ** string, char const * append) { size_t slen = (*string != NULL) ? strlen(*string) : 0; size_t alen = (append != NULL) ? strlen(append) : 0; char * p; if(alen == 0) return 0; if((p = realloc(*string, slen + alen + 1)) == NULL) return -_pkgconfig_error(1, "%s", strerror(errno)); *string = p; strcpy(*string + slen, append); return 0; } /* string_append_length */ static int _string_append_length(char ** string, char const * append, size_t length) { size_t slen = (*string != NULL) ? strlen(*string) : 0; size_t alen = (append != NULL) ? length : 0; char * p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\", %zu)\n", __func__, *string, append, length); #endif if(alen == 0) return 0; if((p = realloc(*string, slen + alen + 1)) == NULL) return -_pkgconfig_error(1, "%s", strerror(errno)); *string = p; strncpy(*string + slen, append, alen); (*string)[slen + alen] = '\0'; return 0; } /* lists */ static PkgList * _pkglist_new(PkgList ** pkglist) { if (*pkglist != NULL) return *pkglist; if((*pkglist = malloc(sizeof(PkgList))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return NULL; } (*pkglist)->len = 0; (*pkglist)->cap = 0; (*pkglist)->data = NULL; return *pkglist; } static void * _pkglist_get(PkgList *pkglist, size_t i) { if (i >= pkglist->len) return NULL; return pkglist->data[i]->data; } static void _pkglist_delete(PkgList *pkglist) { size_t i; if (pkglist == NULL) return; for (i = 0; i < pkglist->len; i++) { if (pkglist->data[i]->freefn != NULL) pkglist->data[i]->freefn(_pkglist_get(pkglist,i)); } free(pkglist); } static void _pkglist_append(PkgList *pkglist, void *data, void (*freefn)(void *)) { ListData *ldata; if (pkglist->cap <= pkglist->len) { pkglist->cap |= 1; pkglist->cap *= 2; if ((pkglist->data = realloc(pkglist->data, pkglist->cap * sizeof (ListData))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return; } } if ((ldata = malloc(sizeof(ListData))) == NULL) { _pkgconfig_error(1, "%s", strerror(errno)); return; } ldata->data = data; ldata->freefn = freefn; pkglist->data[pkglist->len++] = ldata; } /* usage */ static int _usage(int brief) { if(brief) fputs("Usage: " PROGNAME " [-?] [--version] [--modversion]\n", stderr); else fputs("Usage: " PROGNAME " [OPTIONS...] [PACKAGES...]\n" " --cflags Output all pre-processor and compiler flags\n" " --cflags-only-I Output -I flags\n" " --cflags-only-other Output non -I flags\n" " --libs Output all linker flags\n" " --libs_only_L Ouput -L flags\n" " --libs_only_l Ouput -l flags\n" " --libs_only_other other libs (e.g. -pthread)\n" " --modversion Output version for package\n" " --print-requires Output the requires packages\n" " --print-requires_private Output the requires private packages\n" " --static Output linker flags for static linking\n" " --version Output version of " PROGNAME "\n" "\n" "Help options:\n" " -?, --help Show this help message\n" " --usage Display brief usage message\n", stderr); return 1; } /* main */ static int _main_option(PkgConfig * pc, char const * option); static int _main_option_cflags(PkgConfig * pc); static int _main_option_cflags_only_I(PkgConfig * pc); static int _main_option_cflags_only_other(PkgConfig * pc); static int _main_option_exists(PkgConfig * pc); static int _main_option_help(PkgConfig * pc); static int _main_option_libs(PkgConfig * pc); static int _main_option_libs_only_l(PkgConfig * pc); static int _main_option_libs_only_L(PkgConfig * pc); static int _main_option_libs_only_other(PkgConfig * pc); static int _main_option_static(PkgConfig * pc); static int _main_option_usage(PkgConfig * pc); static int _main_option_version(PkgConfig * pc); static int _main_option_modversion(PkgConfig * pc); static int _main_option_print_requires(PkgConfig * pc); static int _main_option_print_requires_private(PkgConfig * pc); static struct { char const * option; int (*callback)(PkgConfig * pc); } _main_options[] = { { "cflags", _main_option_cflags }, { "cflags-only-I", _main_option_cflags_only_I }, { "cflags-only-other", _main_option_cflags_only_other }, { "exists", _main_option_exists }, { "help", _main_option_help }, { "libs", _main_option_libs }, { "libs-only-l", _main_option_libs_only_l }, { "libs-only-L", _main_option_libs_only_L }, { "libs-only-other", _main_option_libs_only_other }, { "modversion", _main_option_modversion }, { "print-requires", _main_option_print_requires }, { "print-requires-private", _main_option_print_requires_private }, { "static", _main_option_static }, { "usage", _main_option_usage }, { "version", _main_option_version } }; int main(int argc, char * argv[]) { PkgConfig pc; int optind; memset(&pc, 0, sizeof(pc)); _pkglist_new(&pc.pkgs); _pkglist_new(&pc.pc_dirs); pc.flags = '\0'; /* getopt() is too complicated for GNU */ /* XXX stupid GNU accepts options even after actual arguments */ /* -- khorben: no want fix it cause this meant as troll */ for(optind = 1; optind < argc; optind++) { if(strcmp(argv[optind], "-?") == 0) return _usage(0); if(strncmp(argv[optind], "--", 2) != 0) break; if(argv[optind][2] == '\0') { optind++; break; } if(_main_option(&pc, argv[optind]) != 0) return 1; } /* check if any package was specified */ if(optind == argc) { fputs("Must specify package names on the command line\n", stderr); return 1; } return _pkgconfig(&pc, argc - optind, &argv[optind]); } static int _main_option(PkgConfig * pc, char const * option) { size_t i; for(i = 0; i < sizeof(_main_options) / sizeof(*_main_options); i++) if(strcmp(_main_options[i].option, &option[2]) == 0) return _main_options[i].callback(pc); fprintf(stderr, "%s: Unknown option\n", option); return 1; } static int _main_option_cflags(PkgConfig * pc) { pc->flags |= PKG_CFLAGS; return 0; } static int _main_option_cflags_only_I(PkgConfig * pc) { pc->flags |= PKG_CFLAGS_ONLY_I; return 0; } static int _main_option_cflags_only_other(PkgConfig * pc) { pc->flags |= PKG_CFLAGS_ONLY_OTHERS; return 0; } static int _main_option_exists(PkgConfig * pc) { pc->flags |= PKG_EXISTS; return 0; } static int _main_option_help(__unused PkgConfig * pc) { return _usage(0); } static int _main_option_libs(PkgConfig * pc) { pc->flags |= PKG_LIBS; return 0; } static int _main_option_libs_only_L(PkgConfig * pc) { pc->flags |= PKG_LIBS_ONLY_L; return 0; } static int _main_option_libs_only_l(PkgConfig * pc) { pc->flags |= PKG_LIBS_ONLY_l; return 0; } static int _main_option_libs_only_other(PkgConfig * pc) { pc->flags |= PKG_LIBS_ONLY_OTHERS; return 0; } static int _main_option_modversion(PkgConfig * pc) { pc->flags |= PKG_MODVERSION; return 0; } static int _main_option_print_requires(PkgConfig * pc) { pc->flags |= PKG_PRINT_REQUIRES; return 0; } static int _main_option_print_requires_private(PkgConfig * pc) { pc->flags |= PKG_PRINT_REQUIRES_PRIV; return 0; } static int _main_option_static(PkgConfig * pc) { pc->flags |= PKG_STATIC; return 0; } static int _main_option_usage(__unused PkgConfig * pc) { return _usage(1); } static int _main_option_version(__unused PkgConfig * pc) { puts("0.26"); exit(0); return -1; }