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 while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { 91 switch (ch) { 92 case 'd': 93 dflag = 1; 94 date = optarg; 95 break; 96 case 'f': 97 updatingfile = optarg; 98 break; 99 case 'h': 100 default: 101 usage(); 102 } 103 } 104 argc -= optind; 105 argv += optind; 106 107 /* Check if passed date has a correct format. */ 108 if (dflag == 1) { 109 linelength = strlen(date); 110 if (linelength != 8) 111 exit(EX_DATAERR); 112 if (strspn(date, "0123456789") != 8) { 113 fprintf(stderr, "unknown date format: %s\n", date); 114 exit(EX_DATAERR); 115 } 116 } 117 118 /* Save the list of passed portnames. */ 119 if (argc != 0) { 120 pflag = 1; 121 while (*argv) { 122 if ((curr = (INSTALLEDPORT *) 123 malloc(sizeof(INSTALLEDPORT))) == NULL) 124 (void)exit(EXIT_FAILURE); 125 strlcpy(curr->name, *argv, strlen(*argv) + 1); 126 curr->next = head; 127 head = curr; 128 (void)*argv++; 129 } 130 } 131 132 /* 133 * UPDATING will be parsed for all installed ports 134 * if no portname is passed. 135 */ 136 if (pflag == 0) { 137 /* Open /var/db/pkg and search for all installed ports. */ 138 if ((dir = opendir(pkgdbpath)) != NULL) { 139 while ((pkgdbdir = readdir(dir)) != NULL) { 140 if (strcmp(pkgdbdir->d_name, ".") != 0 && 141 strcmp(pkgdbdir->d_name, "..") != 0) { 142 143 /* Create path to +CONTENTS file for each installed port */ 144 n = strlcpy(tmp_file, pkgdbpath, sizeof(tmp_file)); 145 n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n); 146 n = strlcat(tmp_file + n, pkgdbdir->d_name, 147 sizeof(tmp_file) - n); 148 if (stat(tmp_file, &attribute) == -1) { 149 fprintf(stderr, "can't open %s: %s\n", 150 tmp_file, strerror(errno)); 151 return EXIT_FAILURE; 152 } 153 if (attribute.st_mode & S_IFREG) 154 continue; 155 (void)strlcat(tmp_file + n, "/", 156 sizeof(tmp_file) - n); 157 (void)strlcat(tmp_file + n, CONTENTS_FNAME, 158 sizeof(tmp_file) - n); 159 160 /* Open +CONTENT file */ 161 fd = fopen(tmp_file, "r"); 162 if (fd == NULL) { 163 fprintf(stderr, "warning: can't open %s: %s\n", 164 tmp_file, strerror(errno)); 165 continue; 166 } 167 168 /* 169 * Parses +CONTENT for ORIGIN line and 170 * put element into linked list. 171 */ 172 while (fgets(originline, maxcharperline, fd) != NULL) { 173 tmpline1 = strstr(originline, origin); 174 if (tmpline1 != NULL) { 175 /* Tmp variable to store port name. */ 176 char *pname; 177 pname = strrchr(originline, (int)':'); 178 pname++; 179 if ((curr = (INSTALLEDPORT *) 180 malloc(sizeof(INSTALLEDPORT))) == NULL) 181 (void)exit(EXIT_FAILURE); 182 if (pname[strlen(pname) - 1] == '\n') 183 pname[strlen(pname) - 1] = '\0'; 184 strlcpy (curr->name, pname, sizeof(curr->name)); 185 curr->next = head; 186 head = curr; 187 } 188 } 189 190 if (ferror(fd)) { 191 fprintf(stderr, "error reading input\n"); 192 exit(EX_IOERR); 193 } 194 195 (void)fclose(fd); 196 } 197 } 198 closedir(dir); 199 } 200 } 201 202 /* Fetch UPDATING file if needed and open file */ 203 if (isURL(updatingfile)) { 204 if ((fd = fetchGetURL(updatingfile, "")) == NULL) { 205 fprintf(stderr, "Error: Unable to get %s: %s\n", 206 updatingfile, fetchLastErrString); 207 exit(EX_UNAVAILABLE); 208 } 209 } 210 else { 211 fd = fopen(updatingfile, "r"); 212 } 213 if (fd == NULL) { 214 fprintf(stderr, "can't open %s: %s\n", 215 updatingfile, strerror(errno)); 216 exit(EX_UNAVAILABLE); 217 } 218 219 /* Parse opened UPDATING file. */ 220 while (fgets(updatingline, maxcharperline, fd) != NULL) { 221 /* No entry is found so far */ 222 if (found == 0) { 223 /* Search for AFFECTS line to parse the portname. */ 224 tmpline1 = strstr(updatingline, affects); 225 226 if (tmpline1 != NULL) { 227 curr = head; 228 while (curr != NULL) { 229 tmpline2 = strstr(updatingline, curr->name); 230 if (tmpline2 != NULL) 231 break; 232 curr = curr->next; 233 } 234 if (tmpline2 != NULL) { 235 /* If -d is set, check if entry is newer than the date. */ 236 if ((dflag == 1) && (strncmp(dateline, date, 8) < 0)) 237 continue; 238 printf("%s", dateline); 239 printf("%s", updatingline); 240 found = 1; 241 } 242 } 243 } 244 /* Search for the end of an entry, if not found print the line. */ 245 else { 246 tmpline1 = strstr(updatingline, end); 247 if (tmpline1 == NULL) 248 printf("%s", updatingline); 249 else { 250 linelength = strlen(updatingline); 251 if (linelength == 10) 252 found = 0; 253 else 254 printf("%s", updatingline); 255 } 256 } 257 /* Save the actual line, it could be a date. */ 258 dateline = strdup(updatingline); 259 } 260 261 if (ferror(fd)) { 262 fprintf(stderr, "error reading input\n"); 263 exit(EX_IOERR); 264 } 265 (void)fclose(fd); 266 267 exit(EX_OK); 268} 269 270int 271usage(void) 272{ 273 fprintf(stderr, 274 "usage: pkg_updating [-h] [-d YYYYMMDD] [-f file] [portname ...]\n"); 275 exit(EX_USAGE); 276} 277 278void 279cleanup(int sig) 280{ 281 if (sig) 282 exit(1); 283} 284