1/*- 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <beat@chruetertee.ch> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Beat Gätzi 7 * ---------------------------------------------------------------------------- 8 */ 9 10#include <sys/cdefs.h> 11__FBSDID("$FreeBSD$"); 12 13 14#include <sys/param.h> 15#include <stdio.h> 16#include <errno.h> 17#include <fetch.h> 18#include <limits.h> 19#include <sysexits.h> 20#include <getopt.h> 21 22#include "lib.h" 23#include "pathnames.h" 24 25typedef struct installedport { 26 struct installedport *next; /* List of installed ports. */ 27 char name[LINE_MAX]; /* Name of the installed port. */ 28} INSTALLEDPORT; 29 30int usage(void); 31 32static char opts[] = "d:f:h"; 33static struct option longopts[] = { 34 { "date", required_argument, NULL, 'd' }, 35 { "file", required_argument, NULL, 'f' }, 36 { "help", no_argument, NULL, 'h' }, 37 { NULL, 0, NULL, 0 }, 38}; 39 40/* 41 * Parse /usr/port/UPDATING for corresponding entries. If no argument is 42 * passed to pkg_updating all entries for all installed ports are displayed. 43 * If a list of portnames is passed to pkg_updating only entries for the 44 * given portnames are displayed. Use the -d option to define that only newer 45 * entries as this date are shown. 46 */ 47int 48main(int argc, char *argv[]) 49{ 50 /* Keyword for searching portname in UPDATING. */ 51 const char *affects = "AFFECTS"; 52 /* Indicate a date -> end of a entry. Will fail on 2100-01-01... */ 53 const char *end = "20"; 54 /* Keyword for searching origin portname of installed port. */ 55 const char *origin = "@comment ORIGIN:"; 56 const char *pkgdbpath = LOG_DIR; /* Location of pkgdb */ 57 const char *updatingfile = UPDATING; /* Location of UPDATING */ 58 59 char *date = NULL; /* Passed -d argument */ 60 char *dateline = NULL; /* Saved date of an entry */ 61 /* Tmp lines for parsing file */ 62 char *tmpline1 = NULL; 63 char *tmpline2 = NULL; 64 65 char originline[LINE_MAX]; /* Line of +CONTENTS */ 66 /* Temporary variable to create path to +CONTENTS for installed ports. */ 67 char tmp_file[MAXPATHLEN]; 68 char updatingline[LINE_MAX]; /* Line of UPDATING */ 69 70 int ch; /* Char used by getopt */ 71 int found = 0; /* Found an entry */ 72 int linelength; /* Length of parsed line */ 73 int maxcharperline = LINE_MAX; /* Max chars per line */ 74 int dflag = 0; /* -d option set */ 75 /* If pflag = 0 UPDATING will be checked for all installed ports. */ 76 int pflag = 0; 77 78 size_t n; /* Offset to create path */ 79 80 struct dirent *pkgdbdir; /* pkgdb directory */ 81 struct stat attribute; /* attribute of pkgdb element */ 82 83 /* Needed nodes for linked list with installed ports. */ 84 INSTALLEDPORT *head = (INSTALLEDPORT *) NULL; 85 INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL; 86 87 DIR *dir; 88 FILE *fd; 89 90 warnpkgng(); 91 while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { 92 switch (ch) { 93 case 'd': 94 dflag = 1; 95 date = optarg; 96 break; 97 case 'f': 98 updatingfile = optarg; 99 break; 100 case 'h': 101 default: 102 usage(); 103 } 104 } 105 argc -= optind; 106 argv += optind; 107 108 /* Check if passed date has a correct format. */ 109 if (dflag == 1) { 110 linelength = strlen(date); 111 if (linelength != 8) 112 exit(EX_DATAERR); 113 if (strspn(date, "0123456789") != 8) { 114 fprintf(stderr, "unknown date format: %s\n", date); 115 exit(EX_DATAERR); 116 } 117 } 118 119 /* Save the list of passed portnames. */ 120 if (argc != 0) { 121 pflag = 1; 122 while (*argv) { 123 if ((curr = (INSTALLEDPORT *) 124 malloc(sizeof(INSTALLEDPORT))) == NULL) 125 (void)exit(EXIT_FAILURE); 126 strlcpy(curr->name, *argv, strlen(*argv) + 1); 127 curr->next = head; 128 head = curr; 129 (void)*argv++; 130 } 131 } 132 133 /* 134 * UPDATING will be parsed for all installed ports 135 * if no portname is passed. 136 */ 137 if (pflag == 0) { 138 /* Open /var/db/pkg and search for all installed ports. */ 139 if ((dir = opendir(pkgdbpath)) != NULL) { 140 while ((pkgdbdir = readdir(dir)) != NULL) { 141 if (strcmp(pkgdbdir->d_name, ".") != 0 && 142 strcmp(pkgdbdir->d_name, "..") != 0) { 143 144 /* Create path to +CONTENTS file for each installed port */ 145 n = strlcpy(tmp_file, pkgdbpath, sizeof(tmp_file)); 146 n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n); 147 n = strlcat(tmp_file + n, pkgdbdir->d_name, 148 sizeof(tmp_file) - n); 149 if (stat(tmp_file, &attribute) == -1) { 150 fprintf(stderr, "can't open %s: %s\n", 151 tmp_file, strerror(errno)); 152 return EXIT_FAILURE; 153 } 154 if (attribute.st_mode & S_IFREG) 155 continue; 156 (void)strlcat(tmp_file + n, "/", 157 sizeof(tmp_file) - n); 158 (void)strlcat(tmp_file + n, CONTENTS_FNAME, 159 sizeof(tmp_file) - n); 160 161 /* Open +CONTENT file */ 162 fd = fopen(tmp_file, "r"); 163 if (fd == NULL) { 164 fprintf(stderr, "warning: can't open %s: %s\n", 165 tmp_file, strerror(errno)); 166 continue; 167 } 168 169 /* 170 * Parses +CONTENT for ORIGIN line and 171 * put element into linked list. 172 */ 173 while (fgets(originline, maxcharperline, fd) != NULL) { 174 tmpline1 = strstr(originline, origin); 175 if (tmpline1 != NULL) { 176 /* Tmp variable to store port name. */ 177 char *pname; 178 pname = strrchr(originline, (int)':'); 179 pname++; 180 if ((curr = (INSTALLEDPORT *) 181 malloc(sizeof(INSTALLEDPORT))) == NULL) 182 (void)exit(EXIT_FAILURE); 183 if (pname[strlen(pname) - 1] == '\n') 184 pname[strlen(pname) - 1] = '\0'; 185 strlcpy (curr->name, pname, sizeof(curr->name)); 186 curr->next = head; 187 head = curr; 188 } 189 } 190 191 if (ferror(fd)) { 192 fprintf(stderr, "error reading input\n"); 193 exit(EX_IOERR); 194 } 195 196 (void)fclose(fd); 197 } 198 } 199 closedir(dir); 200 } 201 } 202 203 /* Fetch UPDATING file if needed and open file */ 204 if (isURL(updatingfile)) { 205 if ((fd = fetchGetURL(updatingfile, "")) == NULL) { 206 fprintf(stderr, "Error: Unable to get %s: %s\n", 207 updatingfile, fetchLastErrString); 208 exit(EX_UNAVAILABLE); 209 } 210 } 211 else { 212 fd = fopen(updatingfile, "r"); 213 } 214 if (fd == NULL) { 215 fprintf(stderr, "can't open %s: %s\n", 216 updatingfile, strerror(errno)); 217 exit(EX_UNAVAILABLE); 218 } 219 220 /* Parse opened UPDATING file. */ 221 while (fgets(updatingline, maxcharperline, fd) != NULL) { 222 /* No entry is found so far */ 223 if (found == 0) { 224 /* Search for AFFECTS line to parse the portname. */ 225 tmpline1 = strstr(updatingline, affects); 226 227 if (tmpline1 != NULL) { 228 curr = head; 229 while (curr != NULL) { 230 tmpline2 = strstr(updatingline, curr->name); 231 if (tmpline2 != NULL) 232 break; 233 curr = curr->next; 234 } 235 if (tmpline2 != NULL) { 236 /* If -d is set, check if entry is newer than the date. */ 237 if ((dflag == 1) && (strncmp(dateline, date, 8) < 0)) 238 continue; 239 printf("%s", dateline); 240 printf("%s", updatingline); 241 found = 1; 242 } 243 } 244 } 245 /* Search for the end of an entry, if not found print the line. */ 246 else { 247 tmpline1 = strstr(updatingline, end); 248 if (tmpline1 == NULL) 249 printf("%s", updatingline); 250 else { 251 linelength = strlen(updatingline); 252 if (linelength == 10) 253 found = 0; 254 else 255 printf("%s", updatingline); 256 } 257 } 258 /* Save the actual line, it could be a date. */ 259 dateline = strdup(updatingline); 260 } 261 262 if (ferror(fd)) { 263 fprintf(stderr, "error reading input\n"); 264 exit(EX_IOERR); 265 } 266 (void)fclose(fd); 267 268 exit(EX_OK); 269} 270 271int 272usage(void) 273{ 274 fprintf(stderr, 275 "usage: pkg_updating [-h] [-d YYYYMMDD] [-f file] [portname ...]\n"); 276 exit(EX_USAGE); 277} 278 279void 280cleanup(int sig) 281{ 282 if (sig) 283 exit(1); 284} 285