2 * pacsort.c - a sort utility implementing alpm_pkg_vercmp
4 * Copyright (c) 2010-2011 Pacman Development Team <pacman-dev@archlinux.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
42 static struct options_t
{
50 /* A quick and dirty implementation derived from glibc */
51 static size_t strnlen(const char *s
, size_t max
)
53 register const char *p
;
54 for(p
= s
; *p
&& max
--; ++p
);
58 char *strndup(const char *s
, size_t n
)
60 size_t len
= strnlen(s
, n
);
61 char *new = (char *) malloc(len
+ 1);
67 return (char *)memcpy(new, s
, len
);
71 static struct buffer_t
*buffer_new(size_t initial_size
)
75 buf
= calloc(1, sizeof(*buf
));
80 buf
->mem
= calloc(initial_size
, sizeof(char));
87 buf
->maxlen
= initial_size
;
92 static void buffer_free(struct buffer_t
*buf
)
105 static int buffer_grow(struct buffer_t
*buffer
)
107 size_t newsz
= buffer
->maxlen
* 2.5;
108 buffer
->mem
= realloc(buffer
->mem
, newsz
* sizeof(char));
112 buffer
->maxlen
= newsz
;
117 static struct list_t
*list_new(size_t initial_size
)
121 list
= calloc(1, sizeof(struct list_t
));
126 list
->list
= calloc(initial_size
, sizeof(char *));
132 list
->maxcount
= initial_size
;
137 static int list_grow(struct list_t
*list
)
139 size_t newsz
= list
->maxcount
* 2.5;
140 list
->list
= realloc(list
->list
, newsz
* sizeof(char *));
145 list
->maxcount
= newsz
;
150 static int list_add(struct list_t
*list
, char *name
)
156 if(list
->count
+ 1 >= list
->maxcount
) {
157 if(list_grow(list
) != 0) {
162 list
->list
[list
->count
] = name
;
168 static void list_free(struct list_t
*list
)
177 for(i
= 0; i
< list
->count
; i
++) {
185 static char *explode(struct buffer_t
*buffer
, struct list_t
*list
)
187 char *name
, *ptr
, *end
;
188 const char linedelim
= opts
.null
? '\0' : '\n';
191 while((end
= memchr(ptr
, linedelim
, &buffer
->mem
[buffer
->len
] - ptr
))) {
194 list_add(list
, name
);
201 static int splitfile(FILE *stream
, struct buffer_t
*buffer
, struct list_t
*list
)
206 while(!feof(stream
)) {
207 /* check if a read of BUFSIZ chars will overflow */
208 if (buffer
->len
+ BUFSIZ
+ 1 >= buffer
->maxlen
) {
209 if(buffer_grow(buffer
) != 0) {
214 nread
= fread(&buffer
->mem
[buffer
->len
], 1, BUFSIZ
, stream
);
218 buffer
->len
+= nread
;
220 if((ptr
= explode(buffer
, list
)) == NULL
) {
224 if(ptr
!= buffer
->mem
) {
225 /* realign the data in the buffer */
226 buffer
->len
= &buffer
->mem
[buffer
->len
] - ptr
;
227 memmove(&buffer
->mem
[0], ptr
, buffer
->len
+ 1);
232 char *name
= strndup(buffer
->mem
, buffer
->len
+ 1);
233 if(list_add(list
, name
) != 0) {
241 /* returns a pointer to the nth column of a string without being destructive */
242 static const char *nth_column(const char *string
)
244 const char *prev
, *ptr
;
248 for(col
= 1; ptr
&& col
<= opts
.sortkey
; col
++) {
250 ptr
= strchr(ptr
, opts
.delim
);
259 static int vercmp(const void *p1
, const void *p2
)
261 const char *name1
, *name2
;
263 name1
= *(const char **)p1
;
264 name2
= *(const char **)p2
;
266 if(opts
.sortkey
== 0) {
267 return opts
.order
* alpm_pkg_vercmp(name1
, name2
);
269 return opts
.order
* alpm_pkg_vercmp(nth_column(name1
), nth_column(name2
));
273 static char escape_char(const char *string
)
275 const size_t len
= strlen(string
);
277 if(!string
|| len
> 2) {
285 if(*string
!= '\\') {
303 static void usage(void)
305 fprintf(stderr
, "pacsort v" PACKAGE_VERSION
"\n"
306 "Usage: pacsort [options] [files...]\n\n"
307 " -h, --help display this help message\n"
308 " -k, --key <index> sort input starting on specified column\n"
309 " -r, --reverse sort in reverse order (default: oldest to newest)\n"
310 " -t, --separator <sep> specify field separator (default: space)\n"
311 " -z, --null lines end with null bytes, not newlines\n\n");
314 static int parse_options(int argc
, char **argv
)
318 static const struct option opttable
[] = {
319 {"help", no_argument
, 0, 'h'},
320 {"key", required_argument
, 0, 'k'},
321 {"reverse", no_argument
, 0, 'r'},
322 {"separator", required_argument
, 0, 't'},
323 {"null", no_argument
, 0, 'z'},
327 while((opt
= getopt_long(argc
, argv
, "hk:rt:z", opttable
, NULL
)) != -1) {
332 opts
.sortkey
= (int)strtol(optarg
, NULL
, 10);
333 if(opts
.sortkey
<= 0) {
334 fprintf(stderr
, "error: invalid sort key -- %s\n", optarg
);
342 opts
.delim
= escape_char(optarg
);
343 if(opts
.delim
== -1) {
344 fprintf(stderr
, "error: invalid field separator -- `%s'\n", optarg
);
359 int main(int argc
, char *argv
[])
362 struct buffer_t
*buffer
;
365 /* option defaults */
371 if(parse_options(argc
, argv
) != 0) {
376 list
= list_new(100);
377 buffer
= buffer_new(BUFSIZ
* 3);
380 if(splitfile(stdin
, buffer
, list
) != 0) {
381 fprintf(stderr
, "%s: memory exhausted\n", argv
[0]);
385 while(optind
< argc
) {
386 FILE *input
= fopen(argv
[optind
], "r");
388 if(splitfile(input
, buffer
, list
) != 0) {
389 fprintf(stderr
, "%s: memory exhausted\n", argv
[0]);
394 fprintf(stderr
, "%s: %s: %s\n", argv
[0], argv
[optind
], strerror(errno
));
401 const char linedelim
= opts
.null
? '\0' : '\n';
402 qsort(list
->list
, list
->count
, sizeof(char *), vercmp
);
403 for(i
= 0; i
< list
->count
; i
++) {
404 printf("%s%c", list
->list
[i
], linedelim
);
414 /* vim: set ts=2 sw=2 noet: */