1173295Skrion/*- 2173295Skrion * ---------------------------------------------------------------------------- 3173295Skrion * "THE BEER-WARE LICENSE" (Revision 42): 4173295Skrion * <beat@chruetertee.ch> wrote this file. As long as you retain this notice you 5173295Skrion * can do whatever you want with this stuff. If we meet some day, and you think 6228976Suqs * this stuff is worth it, you can buy me a beer in return. Beat G��tzi 7173295Skrion * ---------------------------------------------------------------------------- 8173295Skrion */ 9173295Skrion 10173295Skrion#include <sys/cdefs.h> 11173295Skrion__FBSDID("$FreeBSD$"); 12173295Skrion 13202844Sflz 14202844Sflz#include <sys/param.h> 15202844Sflz#include <stdio.h> 16173295Skrion#include <errno.h> 17202844Sflz#include <fetch.h> 18173295Skrion#include <limits.h> 19173295Skrion#include <sysexits.h> 20179433Sflz#include <getopt.h> 21173295Skrion 22222035Sflz#include "lib.h" 23173295Skrion#include "pathnames.h" 24173295Skrion 25173295Skriontypedef struct installedport { 26173295Skrion struct installedport *next; /* List of installed ports. */ 27173295Skrion char name[LINE_MAX]; /* Name of the installed port. */ 28173295Skrion} INSTALLEDPORT; 29173295Skrion 30173295Skrionint usage(void); 31173295Skrion 32179433Sflzstatic char opts[] = "d:f:h"; 33179433Sflzstatic struct option longopts[] = { 34179433Sflz { "date", required_argument, NULL, 'd' }, 35179433Sflz { "file", required_argument, NULL, 'f' }, 36179433Sflz { "help", no_argument, NULL, 'h' }, 37179433Sflz { NULL, 0, NULL, 0 }, 38179433Sflz}; 39179433Sflz 40173295Skrion/* 41173295Skrion * Parse /usr/port/UPDATING for corresponding entries. If no argument is 42173295Skrion * passed to pkg_updating all entries for all installed ports are displayed. 43173295Skrion * If a list of portnames is passed to pkg_updating only entries for the 44173295Skrion * given portnames are displayed. Use the -d option to define that only newer 45173295Skrion * entries as this date are shown. 46173295Skrion */ 47173295Skrionint 48173295Skrionmain(int argc, char *argv[]) 49173295Skrion{ 50173295Skrion /* Keyword for searching portname in UPDATING. */ 51173295Skrion const char *affects = "AFFECTS"; 52173295Skrion /* Indicate a date -> end of a entry. Will fail on 2100-01-01... */ 53173295Skrion const char *end = "20"; 54173295Skrion /* Keyword for searching origin portname of installed port. */ 55173295Skrion const char *origin = "@comment ORIGIN:"; 56173295Skrion const char *pkgdbpath = LOG_DIR; /* Location of pkgdb */ 57173295Skrion const char *updatingfile = UPDATING; /* Location of UPDATING */ 58173295Skrion 59173295Skrion char *date = NULL; /* Passed -d argument */ 60173295Skrion char *dateline = NULL; /* Saved date of an entry */ 61173295Skrion /* Tmp lines for parsing file */ 62173295Skrion char *tmpline1 = NULL; 63173295Skrion char *tmpline2 = NULL; 64173295Skrion 65173295Skrion char originline[LINE_MAX]; /* Line of +CONTENTS */ 66173295Skrion /* Temporary variable to create path to +CONTENTS for installed ports. */ 67173295Skrion char tmp_file[MAXPATHLEN]; 68173295Skrion char updatingline[LINE_MAX]; /* Line of UPDATING */ 69173295Skrion 70173295Skrion int ch; /* Char used by getopt */ 71173295Skrion int found = 0; /* Found an entry */ 72173295Skrion int linelength; /* Length of parsed line */ 73173295Skrion int maxcharperline = LINE_MAX; /* Max chars per line */ 74173295Skrion int dflag = 0; /* -d option set */ 75173295Skrion /* If pflag = 0 UPDATING will be checked for all installed ports. */ 76173295Skrion int pflag = 0; 77173295Skrion 78173295Skrion size_t n; /* Offset to create path */ 79173295Skrion 80173295Skrion struct dirent *pkgdbdir; /* pkgdb directory */ 81173295Skrion struct stat attribute; /* attribute of pkgdb element */ 82173295Skrion 83173295Skrion /* Needed nodes for linked list with installed ports. */ 84173295Skrion INSTALLEDPORT *head = (INSTALLEDPORT *) NULL; 85173295Skrion INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL; 86173295Skrion 87173295Skrion DIR *dir; 88173295Skrion FILE *fd; 89173295Skrion 90241830Seadler warnpkgng(); 91179433Sflz while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { 92173295Skrion switch (ch) { 93173295Skrion case 'd': 94173295Skrion dflag = 1; 95173295Skrion date = optarg; 96173295Skrion break; 97173295Skrion case 'f': 98173295Skrion updatingfile = optarg; 99173295Skrion break; 100179433Sflz case 'h': 101173295Skrion default: 102173295Skrion usage(); 103173295Skrion } 104173295Skrion } 105173295Skrion argc -= optind; 106173295Skrion argv += optind; 107173295Skrion 108173295Skrion /* Check if passed date has a correct format. */ 109173295Skrion if (dflag == 1) { 110173295Skrion linelength = strlen(date); 111173295Skrion if (linelength != 8) 112173295Skrion exit(EX_DATAERR); 113173295Skrion if (strspn(date, "0123456789") != 8) { 114173295Skrion fprintf(stderr, "unknown date format: %s\n", date); 115173295Skrion exit(EX_DATAERR); 116173295Skrion } 117173295Skrion } 118173295Skrion 119173295Skrion /* Save the list of passed portnames. */ 120173295Skrion if (argc != 0) { 121173295Skrion pflag = 1; 122173295Skrion while (*argv) { 123202844Sflz if ((curr = (INSTALLEDPORT *) 124173295Skrion malloc(sizeof(INSTALLEDPORT))) == NULL) 125173295Skrion (void)exit(EXIT_FAILURE); 126202844Sflz strlcpy(curr->name, *argv, strlen(*argv) + 1); 127173295Skrion curr->next = head; 128173295Skrion head = curr; 129173295Skrion (void)*argv++; 130173295Skrion } 131173295Skrion } 132173295Skrion 133173295Skrion /* 134173295Skrion * UPDATING will be parsed for all installed ports 135173295Skrion * if no portname is passed. 136173295Skrion */ 137173295Skrion if (pflag == 0) { 138173295Skrion /* Open /var/db/pkg and search for all installed ports. */ 139202844Sflz if ((dir = opendir(pkgdbpath)) != NULL) { 140173295Skrion while ((pkgdbdir = readdir(dir)) != NULL) { 141173295Skrion if (strcmp(pkgdbdir->d_name, ".") != 0 && 142202844Sflz strcmp(pkgdbdir->d_name, "..") != 0) { 143173295Skrion 144173295Skrion /* Create path to +CONTENTS file for each installed port */ 145228670Sdim n = strlcpy(tmp_file, pkgdbpath, sizeof(tmp_file)); 146173295Skrion n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n); 147173295Skrion n = strlcat(tmp_file + n, pkgdbdir->d_name, 148173295Skrion sizeof(tmp_file) - n); 149202844Sflz if (stat(tmp_file, &attribute) == -1) { 150173295Skrion fprintf(stderr, "can't open %s: %s\n", 151173295Skrion tmp_file, strerror(errno)); 152173295Skrion return EXIT_FAILURE; 153173295Skrion } 154202844Sflz if (attribute.st_mode & S_IFREG) 155173295Skrion continue; 156173514Skrion (void)strlcat(tmp_file + n, "/", 157173295Skrion sizeof(tmp_file) - n); 158173514Skrion (void)strlcat(tmp_file + n, CONTENTS_FNAME, 159173514Skrion sizeof(tmp_file) - n); 160173295Skrion 161173295Skrion /* Open +CONTENT file */ 162173295Skrion fd = fopen(tmp_file, "r"); 163202844Sflz if (fd == NULL) { 164173295Skrion fprintf(stderr, "warning: can't open %s: %s\n", 165173295Skrion tmp_file, strerror(errno)); 166173295Skrion continue; 167173295Skrion } 168173295Skrion 169173295Skrion /* 170173295Skrion * Parses +CONTENT for ORIGIN line and 171173295Skrion * put element into linked list. 172173295Skrion */ 173202844Sflz while (fgets(originline, maxcharperline, fd) != NULL) { 174173295Skrion tmpline1 = strstr(originline, origin); 175202844Sflz if (tmpline1 != NULL) { 176173295Skrion /* Tmp variable to store port name. */ 177173295Skrion char *pname; 178173295Skrion pname = strrchr(originline, (int)':'); 179173295Skrion pname++; 180202844Sflz if ((curr = (INSTALLEDPORT *) 181173295Skrion malloc(sizeof(INSTALLEDPORT))) == NULL) 182173295Skrion (void)exit(EXIT_FAILURE); 183179760Sflz if (pname[strlen(pname) - 1] == '\n') 184179760Sflz pname[strlen(pname) - 1] = '\0'; 185228670Sdim strlcpy (curr->name, pname, sizeof(curr->name)); 186173295Skrion curr->next = head; 187173295Skrion head = curr; 188173295Skrion } 189173295Skrion } 190173295Skrion 191202844Sflz if (ferror(fd)) { 192173295Skrion fprintf(stderr, "error reading input\n"); 193173295Skrion exit(EX_IOERR); 194173295Skrion } 195173295Skrion 196173295Skrion (void)fclose(fd); 197173295Skrion } 198173295Skrion } 199173295Skrion closedir(dir); 200173295Skrion } 201173295Skrion } 202173295Skrion 203202844Sflz /* Fetch UPDATING file if needed and open file */ 204202844Sflz if (isURL(updatingfile)) { 205202844Sflz if ((fd = fetchGetURL(updatingfile, "")) == NULL) { 206202844Sflz fprintf(stderr, "Error: Unable to get %s: %s\n", 207202844Sflz updatingfile, fetchLastErrString); 208202844Sflz exit(EX_UNAVAILABLE); 209202844Sflz } 210202844Sflz } 211202844Sflz else { 212202844Sflz fd = fopen(updatingfile, "r"); 213202844Sflz } 214202844Sflz if (fd == NULL) { 215173295Skrion fprintf(stderr, "can't open %s: %s\n", 216202844Sflz updatingfile, strerror(errno)); 217173295Skrion exit(EX_UNAVAILABLE); 218173295Skrion } 219173295Skrion 220173295Skrion /* Parse opened UPDATING file. */ 221202844Sflz while (fgets(updatingline, maxcharperline, fd) != NULL) { 222173295Skrion /* No entry is found so far */ 223173295Skrion if (found == 0) { 224173295Skrion /* Search for AFFECTS line to parse the portname. */ 225173295Skrion tmpline1 = strstr(updatingline, affects); 226173295Skrion 227202844Sflz if (tmpline1 != NULL) { 228173295Skrion curr = head; 229202844Sflz while (curr != NULL) { 230173295Skrion tmpline2 = strstr(updatingline, curr->name); 231202844Sflz if (tmpline2 != NULL) 232173295Skrion break; 233173295Skrion curr = curr->next; 234173295Skrion } 235202844Sflz if (tmpline2 != NULL) { 236173295Skrion /* If -d is set, check if entry is newer than the date. */ 237202844Sflz if ((dflag == 1) && (strncmp(dateline, date, 8) < 0)) 238173295Skrion continue; 239173295Skrion printf("%s", dateline); 240173295Skrion printf("%s", updatingline); 241173295Skrion found = 1; 242173295Skrion } 243173295Skrion } 244173295Skrion } 245173295Skrion /* Search for the end of an entry, if not found print the line. */ 246173295Skrion else { 247173295Skrion tmpline1 = strstr(updatingline, end); 248202844Sflz if (tmpline1 == NULL) 249173295Skrion printf("%s", updatingline); 250173295Skrion else { 251173295Skrion linelength = strlen(updatingline); 252173295Skrion if (linelength == 10) 253173295Skrion found = 0; 254173295Skrion else 255173295Skrion printf("%s", updatingline); 256173295Skrion } 257173295Skrion } 258173295Skrion /* Save the actual line, it could be a date. */ 259173295Skrion dateline = strdup(updatingline); 260173295Skrion } 261173295Skrion 262202844Sflz if (ferror(fd)) { 263173295Skrion fprintf(stderr, "error reading input\n"); 264173295Skrion exit(EX_IOERR); 265173295Skrion } 266173295Skrion (void)fclose(fd); 267173295Skrion 268173295Skrion exit(EX_OK); 269173295Skrion} 270173295Skrion 271173295Skrionint 272173295Skrionusage(void) 273173295Skrion{ 274173295Skrion fprintf(stderr, 275179433Sflz "usage: pkg_updating [-h] [-d YYYYMMDD] [-f file] [portname ...]\n"); 276173295Skrion exit(EX_USAGE); 277173295Skrion} 278173514Skrion 279173514Skrionvoid 280173514Skrioncleanup(int sig) 281173514Skrion{ 282173514Skrion if (sig) 283173514Skrion exit(1); 284173514Skrion} 285