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