libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * pacsort.c - a sort utility implementing alpm_pkg_vercmp 00003 * 00004 * Copyright (c) 2010-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 #include <errno.h> 00021 #include <getopt.h> 00022 #include <stdio.h> 00023 #include <stdlib.h> 00024 #include <string.h> 00025 00026 #include <alpm.h> 00027 00028 #define DELIM ' ' 00029 00030 struct buffer_t { 00031 char *mem; 00032 size_t len; 00033 size_t maxlen; 00034 }; 00035 00036 struct list_t { 00037 char **list; 00038 size_t count; 00039 size_t maxcount; 00040 }; 00041 00042 static struct options_t { 00043 int order; 00044 int sortkey; 00045 int null; 00046 char delim; 00047 } opts; 00048 00049 #ifndef HAVE_STRNDUP 00050 /* A quick and dirty implementation derived from glibc */ 00051 static size_t strnlen(const char *s, size_t max) 00052 { 00053 register const char *p; 00054 for(p = s; *p && max--; ++p); 00055 return (p - s); 00056 } 00057 00058 char *strndup(const char *s, size_t n) 00059 { 00060 size_t len = strnlen(s, n); 00061 char *new = (char *) malloc(len + 1); 00062 00063 if(new == NULL) 00064 return NULL; 00065 00066 new[len] = '\0'; 00067 return (char *)memcpy(new, s, len); 00068 } 00069 #endif 00070 00071 static struct buffer_t *buffer_new(size_t initial_size) 00072 { 00073 struct buffer_t *buf; 00074 00075 buf = calloc(1, sizeof(*buf)); 00076 if(!buf) { 00077 return NULL; 00078 } 00079 00080 buf->mem = calloc(initial_size, sizeof(char)); 00081 if(!buf->mem) { 00082 free(buf); 00083 return NULL; 00084 } 00085 00086 buf->len = 0; 00087 buf->maxlen = initial_size; 00088 00089 return buf; 00090 } 00091 00092 static void buffer_free(struct buffer_t *buf) 00093 { 00094 if(!buf) { 00095 return; 00096 } 00097 00098 if(buf->mem) { 00099 free(buf->mem); 00100 } 00101 00102 free(buf); 00103 } 00104 00105 static int buffer_grow(struct buffer_t *buffer) 00106 { 00107 size_t newsz = buffer->maxlen * 2.5; 00108 buffer->mem = realloc(buffer->mem, newsz * sizeof(char)); 00109 if(!buffer->mem) { 00110 return 1; 00111 } 00112 buffer->maxlen = newsz; 00113 00114 return 0; 00115 } 00116 00117 static struct list_t *list_new(size_t initial_size) 00118 { 00119 struct list_t *list; 00120 00121 list = calloc(1, sizeof(struct list_t)); 00122 if(!list) { 00123 return NULL; 00124 } 00125 00126 list->list = calloc(initial_size, sizeof(char *)); 00127 if(!list->list) { 00128 free(list); 00129 return NULL; 00130 } 00131 00132 list->maxcount = initial_size; 00133 00134 return list; 00135 } 00136 00137 static int list_grow(struct list_t *list) 00138 { 00139 size_t newsz = list->maxcount * 2.5; 00140 list->list = realloc(list->list, newsz * sizeof(char *)); 00141 if(!list->list) { 00142 return 1; 00143 } 00144 00145 list->maxcount = newsz; 00146 00147 return 0; 00148 } 00149 00150 static int list_add(struct list_t *list, char *name) 00151 { 00152 if(!list || !name) { 00153 return 1; 00154 } 00155 00156 if(list->count + 1 >= list->maxcount) { 00157 if(list_grow(list) != 0) { 00158 return 1; 00159 } 00160 } 00161 00162 list->list[list->count] = name; 00163 list->count++; 00164 00165 return 0; 00166 } 00167 00168 static void list_free(struct list_t *list) 00169 { 00170 size_t i; 00171 00172 if(!list) { 00173 return; 00174 } 00175 00176 if(list->list) { 00177 for(i = 0; i < list->count; i++) { 00178 free(list->list[i]); 00179 } 00180 free(list->list); 00181 } 00182 free(list); 00183 } 00184 00185 static char *explode(struct buffer_t *buffer, struct list_t *list) 00186 { 00187 char *name, *ptr, *end; 00188 const char linedelim = opts.null ? '\0' : '\n'; 00189 00190 ptr = buffer->mem; 00191 while((end = memchr(ptr, linedelim, &buffer->mem[buffer->len] - ptr))) { 00192 *end = '\0'; 00193 name = strdup(ptr); 00194 list_add(list, name); 00195 ptr = end + 1; 00196 } 00197 00198 return ptr; 00199 } 00200 00201 static int splitfile(FILE *stream, struct buffer_t *buffer, struct list_t *list) 00202 { 00203 size_t nread; 00204 char *ptr; 00205 00206 while(!feof(stream)) { 00207 /* check if a read of BUFSIZ chars will overflow */ 00208 if (buffer->len + BUFSIZ + 1 >= buffer->maxlen) { 00209 if(buffer_grow(buffer) != 0) { 00210 return 1; 00211 } 00212 } 00213 00214 nread = fread(&buffer->mem[buffer->len], 1, BUFSIZ, stream); 00215 if(nread == 0) { 00216 break; /* EOF */ 00217 } 00218 buffer->len += nread; 00219 00220 if((ptr = explode(buffer, list)) == NULL) { 00221 return 1; 00222 } 00223 00224 if(ptr != buffer->mem) { 00225 /* realign the data in the buffer */ 00226 buffer->len = &buffer->mem[buffer->len] - ptr; 00227 memmove(&buffer->mem[0], ptr, buffer->len + 1); 00228 } 00229 } 00230 00231 if(buffer->len) { 00232 char *name = strndup(buffer->mem, buffer->len + 1); 00233 if(list_add(list, name) != 0) { 00234 return 1; 00235 } 00236 } 00237 00238 return 0; 00239 } 00240 00241 /* returns a pointer to the nth column of a string without being destructive */ 00242 static const char *nth_column(const char *string) 00243 { 00244 const char *prev, *ptr; 00245 int col; 00246 00247 ptr = prev = string; 00248 for(col = 1; ptr && col <= opts.sortkey; col++) { 00249 prev = ptr; 00250 ptr = strchr(ptr, opts.delim); 00251 if(ptr) { 00252 ptr++; 00253 } 00254 } 00255 00256 return prev; 00257 } 00258 00259 static int vercmp(const void *p1, const void *p2) 00260 { 00261 const char *name1, *name2; 00262 00263 name1 = *(const char **)p1; 00264 name2 = *(const char **)p2; 00265 00266 if(opts.sortkey == 0) { 00267 return opts.order * alpm_pkg_vercmp(name1, name2); 00268 } else { 00269 return opts.order * alpm_pkg_vercmp(nth_column(name1), nth_column(name2)); 00270 } 00271 } 00272 00273 static char escape_char(const char *string) 00274 { 00275 const size_t len = strlen(string); 00276 00277 if(!string || len > 2) { 00278 return -1; 00279 } 00280 00281 if(len == 1) { 00282 return *string; 00283 } 00284 00285 if(*string != '\\') { 00286 return -1; 00287 } 00288 00289 switch(string[1]) { 00290 case 't': 00291 return '\t'; 00292 case 'n': 00293 return '\n'; 00294 case 'v': 00295 return '\v'; 00296 case '0': 00297 return '\0'; 00298 default: 00299 return -1; 00300 } 00301 } 00302 00303 static void usage(void) 00304 { 00305 fprintf(stderr, "pacsort v" PACKAGE_VERSION "\n" 00306 "Usage: pacsort [options] [files...]\n\n" 00307 " -h, --help display this help message\n" 00308 " -k, --key <index> sort input starting on specified column\n" 00309 " -r, --reverse sort in reverse order (default: oldest to newest)\n" 00310 " -t, --separator <sep> specify field separator (default: space)\n" 00311 " -z, --null lines end with null bytes, not newlines\n\n"); 00312 } 00313 00314 static int parse_options(int argc, char **argv) 00315 { 00316 int opt; 00317 00318 static const struct option opttable[] = { 00319 {"help", no_argument, 0, 'h'}, 00320 {"key", required_argument, 0, 'k'}, 00321 {"reverse", no_argument, 0, 'r'}, 00322 {"separator", required_argument, 0, 't'}, 00323 {"null", no_argument, 0, 'z'}, 00324 {0, 0, 0, 0} 00325 }; 00326 00327 while((opt = getopt_long(argc, argv, "hk:rt:z", opttable, NULL)) != -1) { 00328 switch(opt) { 00329 case 'h': 00330 return 1; 00331 case 'k': 00332 opts.sortkey = (int)strtol(optarg, NULL, 10); 00333 if(opts.sortkey <= 0) { 00334 fprintf(stderr, "error: invalid sort key -- %s\n", optarg); 00335 return 1; 00336 } 00337 break; 00338 case 'r': 00339 opts.order = -1; 00340 break; 00341 case 't': 00342 opts.delim = escape_char(optarg); 00343 if(opts.delim == -1) { 00344 fprintf(stderr, "error: invalid field separator -- `%s'\n", optarg); 00345 return 1; 00346 } 00347 break; 00348 case 'z': 00349 opts.null = 1; 00350 break; 00351 default: 00352 return 1; 00353 } 00354 } 00355 00356 return 0; 00357 } 00358 00359 int main(int argc, char *argv[]) 00360 { 00361 struct list_t *list; 00362 struct buffer_t *buffer; 00363 size_t i; 00364 00365 /* option defaults */ 00366 opts.order = 1; 00367 opts.delim = DELIM; 00368 opts.sortkey = 0; 00369 opts.null = 0; 00370 00371 if(parse_options(argc, argv) != 0) { 00372 usage(); 00373 return 2; 00374 } 00375 00376 list = list_new(100); 00377 buffer = buffer_new(BUFSIZ * 3); 00378 00379 if(optind == argc) { 00380 if(splitfile(stdin, buffer, list) != 0) { 00381 fprintf(stderr, "%s: memory exhausted\n", argv[0]); 00382 return ENOMEM; 00383 } 00384 } else { 00385 while(optind < argc) { 00386 FILE *input = fopen(argv[optind], "r"); 00387 if(input) { 00388 if(splitfile(input, buffer, list) != 0) { 00389 fprintf(stderr, "%s: memory exhausted\n", argv[0]); 00390 return ENOMEM; 00391 } 00392 fclose(input); 00393 } else { 00394 fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno)); 00395 } 00396 optind++; 00397 } 00398 } 00399 00400 if(list->count) { 00401 const char linedelim = opts.null ? '\0' : '\n'; 00402 qsort(list->list, list->count, sizeof(char *), vercmp); 00403 for(i = 0; i < list->count; i++) { 00404 printf("%s%c", list->list[i], linedelim); 00405 } 00406 } 00407 00408 list_free(list); 00409 buffer_free(buffer); 00410 00411 return 0; 00412 } 00413 00414 /* vim: set ts=2 sw=2 noet: */