#include #include #include #include #include #include #include #include #include #include "packages.h" static struct package_record packages[MAX_PACKAGES]; static int max_packages = 0; static int max_files = 0; static int verbose = 0; static struct { unsigned int packages; unsigned int packages_not_found; } stats = {0, 0}; static char distro_name[DISTRO_NAME_LENGTH]; struct file_record { struct package_file_record pfr; struct file_record * next; int file_num; int duplicate; } * file_base; static struct file_record * add_filename(const char * filename, unsigned int package_num) { struct file_record *new; new = malloc(sizeof(struct file_record)); bzero(new, sizeof(*new)); strncpy(new->pfr.filename, filename, FILENAME_LENGTH); new->pfr.package_num = package_num; new->pfr.tag_base = NULL; max_files++; new->file_num = max_files; new->next = file_base; file_base = new; return new; } int cmp_file(const void *a1, const void *b1) { int n; struct file_record *a, *b; a = *(struct file_record **)a1; b = *(struct file_record **)b1; n = strcmp(a->pfr.filename, b->pfr.filename); /* Same file names, mark the later one found as duplicate */ if (n == 0) { if (a->file_num < b->file_num) { b->duplicate = 1; n = -1; } else { a->duplicate = 1; n = 1; } } return(n); } void sort_filename(void) { struct file_record **basepp, **bpp; struct file_record *frp; if (max_files <= 1) return; /* Create an array with all the filename records */ basepp = malloc(max_files * sizeof(struct file_record *)); for (frp = file_base, bpp = basepp; frp; bpp++, frp = frp->next) *bpp = frp; /* * Sort them. A side effect is that duplicate file * names get marked as duplicate. */ qsort(basepp, max_files, sizeof(struct file_record *), cmp_file); /* * Recreate the sorted list, starting from the end, * skipping any duplicate entries. */ bpp = &basepp[max_files]; file_base = NULL; while (bpp > basepp) { frp = *--bpp; if (frp->duplicate) { --max_files; continue; } frp->next = file_base; file_base = frp; } free(basepp); } static struct package_record * add_package(const char * package_name) { struct package_record * new = &packages[max_packages]; if (max_packages >= MAX_PACKAGES) { errx(1, "maximum number of packages (%d) was exceeded", MAX_PACKAGES); } snprintf(new->package_name, PACKAGE_NAME_LENGTH, "%s", package_name); new->file_base = NULL; stats.packages++; max_packages++; return new; } static int load_distro(const char * list_dir) { DIR *dirp; FILE * list_file; struct dirent *dp; char package_name[PACKAGE_NAME_LENGTH]; char package_version[PACKAGE_VERSION_LENGTH]; char package_release[PACKAGE_RELEASE_LENGTH]; char package_arch[PACKAGE_ARCH_LENGTH]; char filename[FILENAME_LENGTH]; char path[FILENAME_LENGTH]; char host_package [1024]; struct package_record * pkg; char * c; FILE * pkg_file; char line[10240]; int i; if ((dirp = opendir(list_dir)) == NULL) { perror("Opening distro dir"); return -1; } while ((dp = readdir(dirp)) != NULL) { if ((c = strstr(dp->d_name, ".list")) == NULL) continue; snprintf(path, sizeof(path), "%s/%s", list_dir, dp->d_name); if ((list_file = fopen(path, "r")) == NULL) { perror("opening list file"); continue; } snprintf(host_package, (c-dp->d_name)+1, "%s", dp->d_name); pkg = add_package(host_package); while (fgets(filename, sizeof(filename), list_file)) { filename[strlen(filename)-1] = '\0'; add_filename(filename, max_packages-1); } fclose(list_file); } closedir(dirp); sort_filename(); snprintf(filename, sizeof(filename), "%s/%s", list_dir, "distro"); if ((pkg_file = fopen(filename, "r")) == NULL) { perror("Opening packages file"); return -1; } fgets(distro_name, sizeof(distro_name), pkg_file); fclose(pkg_file); /* * Open the file distro/packages and read to get the * package/version/release/arch combos. Note that we * include the release, but ignore it. */ snprintf(filename, sizeof(filename), "%s/%s", list_dir, "packages"); if ((pkg_file = fopen(filename, "r")) == NULL) { perror("Opening packages file"); return -1; } while (fgets(line, sizeof(line), pkg_file)) { /* Format looks like: bsdutils 1:2.17.2-0ubuntu1 0 amd64 */ if ((sscanf(line, "%s %s %s %s\n", package_name, package_version, package_release, package_arch)) == 4) { for (i = 0; i < max_packages; i++) { pkg = &packages[i]; if (strcmp(pkg->package_name, package_name) == 0) { snprintf(pkg->package_version, PACKAGE_VERSION_LENGTH, "%s", package_version); snprintf(pkg->package_arch, PACKAGE_ARCH_LENGTH, "%s", package_arch); break; } } stats.packages_not_found++; } } fclose(pkg_file); return 0; } static int write_data(const char * filename) { FILE * fd; struct file_record * fr; if ((fd = fopen(filename, "w")) == NULL) { err(1, "%s", filename); /* NOT_REACHED */ } if (fwrite(&distro_name, DISTRO_NAME_LENGTH, 1, fd) != 1 || fwrite(&max_packages, sizeof(max_packages), 1, fd) != 1 || fwrite(&max_files, sizeof(max_files), 1, fd) != 1 || fwrite(packages, sizeof(struct package_record), max_packages, fd) != max_packages) err(1, "fwrite()"); for (fr = file_base; fr; fr = fr->next) { if (fwrite(&fr->pfr, sizeof(struct package_file_record), 1, fd) != 1) err(1, "fwrite()"); } fclose(fd); chmod(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (verbose) { printf("Distro: %s\n", distro_name); printf("Max packages: %d\n", max_packages); printf("Max files: %d\n", max_files); } return 0; } static void usage(void) { fprintf(stderr, "Usage: load_distro [-v] -d \n"); exit(1); /* NOT_REACHED */ } int main(int argc, char * argv[]) { char *distrolist_dir = NULL; char filename[FILENAME_LENGTH]; int opt; while ((opt = getopt(argc, argv, "d:v")) != -1) { switch (opt) { case 'd': distrolist_dir = optarg; continue; case 'v': verbose++; continue; default: usage(); /* NOT_REACHED */ } } /* Check for arguments */ if (distrolist_dir == NULL) { warnx("distrolist directory name missing"); usage(); /* NOT_REACHED */ } load_distro(distrolist_dir); snprintf(filename, FILENAME_LENGTH-1, "%s/distro.blob", distrolist_dir); write_data(filename); return 0; }