cascade of code simplifications (2)
[buildtorrent:buildtorrent.git] / buildtorrent.c
1 /*
2 buildtorrent -- torrent file creation program
3 Copyright (C) 2007,2008,2009,2010 Claude Heiland-Allen
4
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 /* define this as GNU source, so that the versionsort will work with scandir */
22 #define _GNU_SOURCE
23
24 #include "config.h"
25
26 #include <getopt.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <inttypes.h>
32 #include <dirent.h>
33 #include <time.h>
34 /* for correct behaviour ensure this is the POSIX and not the GNU basename:
35    "With glibc, one gets the POSIX version of basename() when <libgen.h> is
36     included, and the GNU version otherwise." */
37 #include <libgen.h>
38
39 #include "sha1.h"
40 #include "md5.h"
41
42
43 /******************************************************************************
44 program version string
45 ******************************************************************************/
46 #define bt_version "0.9~git"
47 const char* __version__ = bt_version;
48
49 /******************************************************************************
50 torrent data structure declarations
51 ******************************************************************************/
52
53 /* structure for torrent file data */
54 struct _bt_data;
55 typedef struct _bt_data *bt_data;
56
57 /* error flags */
58 enum _bt_error {
59   BT_ERROR_NONE = 0,
60   BT_ERROR_NULL,
61   BT_ERROR_TYPE,
62   BT_ERROR_MEMORY,
63   BT_ERROR_IO,
64   BT_ERROR_OVERFLOW,
65   BT_ERROR_PARSE
66 };
67 typedef enum _bt_error bt_error;
68
69 /* attempt to support large files */
70 typedef int64_t integer;
71
72 /* constructors */
73 bt_data bt_string(const unsigned int length, const char *data);
74 bt_data bt_integer(const integer number);
75 bt_data bt_list(void);
76 bt_data bt_dictionary(void);
77
78 /* append to a list */
79 bt_error bt_list_append(bt_data list, const bt_data data);
80
81 /* insert into a dictionary, maintaining sorted keys */
82 bt_error bt_dictionary_insert(
83   bt_data dictionary,
84   const unsigned int keylength, const char *keyname,
85   const bt_data data
86 );
87
88 /* deep-copy a structure */
89 bt_data bt_copy(const bt_data data);
90
91 /* destructor */
92 void bt_free(bt_data bd);
93
94 /* write as a torrent file */
95 bt_error bt_write(FILE *f, const bt_data bd);
96
97 /* types of structures */
98 enum _bt_type {
99   BT_TYPE_STRING = 1000,
100   BT_TYPE_INTEGER,
101   BT_TYPE_LIST,
102   BT_TYPE_DICTIONARY
103 };
104 typedef enum _bt_type bt_type;
105
106 /* string structure */
107 struct _bt_string {
108   unsigned int length;
109   char *data;
110 };
111
112 /* integer structure */
113 struct _bt_integer {
114   integer number;
115 };
116
117 /* list structure */
118 struct _bt_list {
119   struct _bt_data *item;
120   struct _bt_list *next;
121 };
122
123 /* dictionary structure */
124 struct _bt_dictionary {
125   struct _bt_string *key;
126   struct _bt_data *value;
127   struct _bt_dictionary *next;
128 };
129
130 /* general structure */
131 struct _bt_data {
132   bt_type type;
133   union {
134     struct _bt_string *string;
135     struct _bt_integer *integer;
136     struct _bt_list *list;
137     struct _bt_dictionary *dictionary;
138   } b;
139 };
140
141 /******************************************************************************
142 abort on memory allocation failure
143 ******************************************************************************/
144 void *bt_malloc(size_t size) {
145   void *mem = malloc(size);
146   if (!mem) {
147     fprintf(stderr, "buildtorrent: out of memory\n");
148     exit(1);
149   }
150   return mem;
151 }
152
153 /******************************************************************************
154 allocate a new string structure
155 ******************************************************************************/
156 bt_data bt_string(const unsigned int length, const char *data) {
157   bt_data bd = bt_malloc(sizeof(struct _bt_data));
158   struct _bt_string *s = bt_malloc(sizeof(struct _bt_string));
159   s->data = bt_malloc(length);
160   s->length = length;
161   memcpy(s->data, data, length);
162   bd->type = BT_TYPE_STRING;
163   bd->b.string = s;
164   return bd;
165 }
166 #define bt_string0(s) bt_string(strlen((s)),(s))
167
168 /******************************************************************************
169 allocate a new integer structure
170 ******************************************************************************/
171 bt_data bt_integer(const integer number) {
172   bt_data bd = bt_malloc(sizeof(struct _bt_data));
173   struct _bt_integer *n = bt_malloc(sizeof(struct _bt_integer));
174   n->number = number;
175   bd->type = BT_TYPE_INTEGER;
176   bd->b.integer = n;
177   return bd;
178 }
179
180 /******************************************************************************
181 allocate a new empty list structure
182 invariant: bd->b.list != NULL && last(bd->b.list).{item, next} == NULL
183 ******************************************************************************/
184 bt_data bt_list(void) {
185   bt_data bd = bt_malloc(sizeof(struct _bt_data));
186   struct _bt_list *l = bt_malloc(sizeof(struct _bt_list));
187   l->item = NULL;
188   l->next = NULL;
189   bd->type = BT_TYPE_LIST;
190   bd->b.list = l;
191   return bd;
192 }
193
194 /******************************************************************************
195 allocate a new empty dictionary structure
196 invariant: bd->b.dictionary != NULL &&
197            last(bd->b.list).{key, value, next} == NULL &&
198            ordered ascending by key
199 ******************************************************************************/
200 bt_data bt_dictionary(void) {
201   bt_data bd = bt_malloc(sizeof(struct _bt_data));
202   struct _bt_dictionary *d = bt_malloc(sizeof(struct _bt_dictionary));
203   d->key = NULL;
204   d->value = NULL;
205   d->next = NULL;
206   bd->type = BT_TYPE_DICTIONARY;
207   bd->b.dictionary = d;
208   return bd;
209 }
210
211 /******************************************************************************
212 append an item to a list
213 the item is not copied
214 returns an error flag
215 ******************************************************************************/
216 bt_error bt_list_append(bt_data list, const bt_data data) {
217   if (!list) {
218     return BT_ERROR_NULL;
219   }
220   if (BT_TYPE_LIST != list->type) {
221     return BT_ERROR_TYPE;
222   }
223   if (!list->b.list) {
224     return BT_ERROR_NULL;
225   }
226   {
227   struct _bt_list *current;
228   struct _bt_list *end = bt_malloc(sizeof(struct _bt_list));
229   end->item = NULL;
230   end->next = NULL;
231   current = list->b.list;
232   while (current->next) {
233     current = current->next;
234   }
235   current->item = data;
236   current->next = end;
237   }
238   return BT_ERROR_NONE;
239 }
240
241 /******************************************************************************
242 insert an item into a dictionary
243 the value is not copied, the key is copied
244 returns an error flag
245 maintains an ascending ordering of key names
246 FIXME: assumes key names are null terminated
247 ******************************************************************************/
248 bt_error bt_dictionary_insert(
249   bt_data dictionary,
250   const unsigned int keylength, const char *keyname,
251   const bt_data data
252 ) {
253   if (!dictionary) {
254     return BT_ERROR_NULL;
255   }
256   if (BT_TYPE_DICTIONARY != dictionary->type) {
257     return BT_ERROR_TYPE;
258   }
259   if (!dictionary->b.dictionary) {
260     return BT_ERROR_NULL;
261   }
262   {
263   struct _bt_dictionary *current;
264   struct _bt_dictionary *lastcurrent = NULL;
265   struct _bt_dictionary *insertee;
266   struct _bt_string *key = bt_malloc(sizeof(struct _bt_string));
267   key->data = bt_malloc(keylength);
268   insertee = bt_malloc(sizeof(struct _bt_dictionary));
269   memcpy(key->data, keyname, keylength);
270   key->length = keylength;
271   insertee->key = key;
272   insertee->value = data;
273   insertee->next = NULL;
274   current = dictionary->b.dictionary;
275   while (
276     current->next && current->key && (0 < strcmp(keyname, current->key->data))
277   ) {
278     lastcurrent = current;
279     current = current->next;
280   }
281   if (lastcurrent) {
282     insertee->next = current;
283     lastcurrent->next = insertee;
284   } else {
285     insertee->next = dictionary->b.dictionary;
286     dictionary->b.dictionary = insertee;
287   }
288   }
289   return BT_ERROR_NONE;
290 }
291
292 #define bt_dictionary_insert0(dict,key,data) \
293 bt_dictionary_insert((dict),strlen((key)),(key),(data))
294
295 /******************************************************************************
296 deep copy a data structure
297 FIXME: doesn't handle dictionaries yet
298 ******************************************************************************/
299 bt_data bt_copy(const bt_data data) {
300   if (!data) {
301     return NULL;
302   }
303   switch (data->type) {
304   case (BT_TYPE_STRING): {
305     return bt_string(data->b.string->length, data->b.string->data);
306   }
307   case (BT_TYPE_INTEGER): {
308     return bt_integer(data->b.integer->number);
309   }
310   case (BT_TYPE_LIST): {
311     struct _bt_list *current;
312     bt_error err;
313     bt_data list = bt_list();
314     if (!list) {
315       return NULL;
316     }
317     current = data->b.list;
318     while (current && current->item) {
319       if ((err = bt_list_append(list, bt_copy(current->item)))) {
320         bt_free(list);
321         return NULL;
322       }
323       current = current->next;
324     }
325     return list;
326   }
327   default: {
328     return NULL;
329   }
330   }
331 }
332
333 /******************************************************************************
334 free a string
335 ******************************************************************************/
336 void bt_free_string(struct _bt_string *s) {
337   if (!s) {
338     return;
339   }
340   if (s->data) {
341     free(s->data);
342   }
343   free(s);
344 }
345
346 /******************************************************************************
347 deep free a data structure
348 ******************************************************************************/
349 void bt_free(bt_data bd) {
350   if (!bd) {
351     return;
352   }
353   switch (bd->type) {
354   case (BT_TYPE_STRING): {
355     if (bd->b.string) {
356       bt_free_string(bd->b.string);
357     }
358     break;
359   }
360   case (BT_TYPE_INTEGER): {
361     if (bd->b.integer) {
362       free(bd->b.integer);
363     }
364     break;
365   }
366   case (BT_TYPE_LIST): {
367     struct _bt_list *current;
368     struct _bt_list *next;
369     current = bd->b.list;
370     while (current) {
371       next = current->next;
372       if (current->item) {
373         bt_free(current->item);
374       }
375       free(current);
376       current = next;
377     }
378     break;
379   }
380   case (BT_TYPE_DICTIONARY): {
381     struct _bt_dictionary *current;
382     struct _bt_dictionary *next;
383     current = bd->b.dictionary;
384     while (current) {
385       next = current->next;
386       if (current->key) {
387         bt_free_string(current->key);
388       }
389       if (current->value) {
390         bt_free(current->value);
391       }
392       free(current);
393       current = next;
394     }
395     break;
396   }
397   default:
398     break;
399   }
400   free(bd);
401 }
402
403 /******************************************************************************
404 write a string in torrent encoding
405 ******************************************************************************/
406 bt_error bt_write_string(FILE *f, const struct _bt_string *s) {
407   if (!s) {
408     return BT_ERROR_NULL;
409   }
410   if (!s->data) {
411     return BT_ERROR_NULL;
412   }
413   if (0 > fprintf(f, "%d:", s->length)) {
414     return BT_ERROR_IO;
415   }
416   if (1 != fwrite(s->data, s->length, 1, f)) {
417     return BT_ERROR_IO;
418   }
419   return BT_ERROR_NONE;
420 }
421
422 /******************************************************************************
423 write a data structure in torrent encoding
424 ******************************************************************************/
425 bt_error bt_write(FILE *f, const bt_data bd) {
426   if (!bd) {
427     return BT_ERROR_NULL;
428   }
429   switch (bd->type) {
430   case (BT_TYPE_STRING): {
431     return bt_write_string(f, bd->b.string);
432     break;
433   }
434   case (BT_TYPE_INTEGER): {
435     if (!bd->b.integer) {
436       return BT_ERROR_NULL;
437     }
438     if (0 > fprintf(f, "i%" PRId64 "e", bd->b.integer->number)) {
439       return BT_ERROR_IO;
440     }
441     break;
442   }
443   case (BT_TYPE_LIST): {
444     struct _bt_list *current;
445     bt_error err;
446     current = bd->b.list;
447     if (0 > fprintf(f, "l")) {
448       return BT_ERROR_IO;
449     }
450     while (current->next) {
451       if (current->item) {
452         if ((err = bt_write(f, current->item))) {
453           return err;
454         }
455       }
456       current = current->next;
457     }
458     if (0 > fprintf(f, "e")) {
459       return BT_ERROR_IO;
460     }
461     break;
462   }
463   case (BT_TYPE_DICTIONARY): {
464     struct _bt_dictionary *current;
465     bt_error err;
466     current = bd->b.dictionary;
467     if (0 > fprintf(f, "d")) {
468       return BT_ERROR_IO;
469     }
470     while (current->next) {
471       if (current->key) {
472         if ((err = bt_write_string(f, current->key))) {
473           return err;
474         }
475         if (!current->value) {
476           return BT_ERROR_NULL;
477         }
478         if ((err = bt_write(f, current->value))) {
479           return err;
480         }
481       }
482       current = current->next;
483     }
484     if (0 > fprintf(f, "e")) {
485       return BT_ERROR_IO;
486     }
487     break;
488   }
489   default: {
490     return BT_ERROR_TYPE;
491     break;
492   }
493   }
494   return BT_ERROR_NONE;
495 }
496
497 /******************************************************************************
498 pretty print torrent data
499 ******************************************************************************/
500 void bt_show(bt_data bd, int piecestoo, int indent, int indentstep, int comma) {
501   int i;
502   if (!bd) {
503     for(i = 0; i < indent; i++) {
504       printf(" ");
505     }
506     printf("NULL%s\n", comma ? "," : "");
507     return;
508   }
509   switch (bd->type) {
510   case (BT_TYPE_STRING): {
511     char strbuf[512];
512     int len = bd->b.string->length > 500 ? 500 : bd->b.string->length;
513     memcpy(strbuf, bd->b.string->data, len);
514     strbuf[len] = '\0';
515     for(i = 0; i < indent; i++) {
516       printf(" ");
517     }
518     printf(
519       "\"%s\"%s%s\n", strbuf, comma ? "," : "", len == 500 ? " // truncated!" : ""
520     );
521     break;
522   }
523   case (BT_TYPE_INTEGER): {
524     for(i = 0; i < indent; i++) {
525       printf(" ");
526     }
527     printf("%" PRId64 "%s\n", bd->b.integer->number, comma ? "," : "");
528     break;
529   }
530   case (BT_TYPE_LIST): {
531     struct _bt_list *current;
532     for(i = 0; i < indent; i++) {
533       printf(" ");
534     }
535     printf("[\n");
536     current = bd->b.list;
537     while (current->next) {
538       bt_show(
539         current->item, piecestoo,
540         indent + indentstep, indentstep,
541         current->next->next != NULL
542       );
543       current = current->next;
544     }
545     for(i = 0; i < indent; i++) {
546       printf(" ");
547     }
548     printf("]%s\n", comma ? "," : "");
549     break;
550   }
551   case (BT_TYPE_DICTIONARY): {
552     struct _bt_dictionary *current;
553     char strbuf[512];
554     int len;
555     for(i = 0; i < indent; i++) {
556       printf(" ");
557     }
558     printf("{\n");
559     current = bd->b.dictionary;
560     while (current->next) {
561       len = current->key->length > 500 ? 500 : current->key->length;
562       memcpy(strbuf, current->key->data, len);
563       strbuf[len] = '\0';
564       for(i = 0; i < indent + indentstep; i++) {
565         printf(" ");
566       }
567       printf("\"%s\" =>%s\n", strbuf, len == 500 ? " // truncated!" : "");
568       if (strcmp("pieces", strbuf) == 0) {
569         if (piecestoo) {
570           printf("----------------------------------------");
571           char *hexdigits = "0123456789abcdef";
572           for (i = 0; i < current->value->b.string->length; i++) {
573             if ((i % 20) == 0) {
574               printf("\n");
575             }
576             printf("%c%c",
577               hexdigits[(current->value->b.string->data[i] & 0xF0) >> 4],
578               hexdigits[(current->value->b.string->data[i] & 0x0F)]
579             );
580           }
581           printf("\n----------------------------------------\n");
582         } else {
583           for(i = 0; i < indent + indentstep + indentstep; i++) {
584             printf(" ");
585           }
586           printf("\"...\"%s // pieces not shown\n", current->next->next ? "," : "");
587         }
588       } else {
589         bt_show(
590           current->value, piecestoo, indent + indentstep + indentstep,
591           indentstep, current->next->next != NULL
592         );
593       }
594       current = current->next;
595     }
596     for(i = 0; i < indent; i++) {
597       printf(" ");
598     }
599     printf("}%s\n", comma ? "," : "");
600     break;
601   }
602   }
603 }
604
605
606 /******************************************************************************
607 file list data structure
608 the first in the list is a dummy
609 ******************************************************************************/
610 struct _bt_file_list {
611   char *file;
612   bt_data path;
613   struct _bt_file_list *next;
614 };
615 typedef struct _bt_file_list *bt_file_list;
616
617 /******************************************************************************
618 create the dummy first list element
619 ******************************************************************************/
620 bt_file_list bt_create_file_list() {
621   bt_file_list flist = bt_malloc(sizeof(struct _bt_file_list));
622   flist->file = NULL;
623   flist->path = NULL;
624   flist->next = NULL;
625   return flist;
626 }
627
628 /******************************************************************************
629 prepend to the file list
630 copies the arguments
631 path may be NULL, but only in single file mode
632 ******************************************************************************/
633 bt_error bt_file_list_prepend(
634   bt_file_list flist, const char *file, bt_data path
635 ) {
636   if (!flist) {
637     return BT_ERROR_NULL;
638   }
639   if (!file) {
640     return BT_ERROR_NULL;
641   }
642   {
643   bt_file_list node = bt_malloc(sizeof(struct _bt_file_list));
644   node->file = bt_malloc(strlen(file) + 1);
645   memcpy(node->file, file, strlen(file) + 1);
646   if (path) {
647     if (!(node->path = bt_copy(path))) {
648       free(node->file);
649       free(node);
650       return BT_ERROR_MEMORY;
651     }
652   } else {
653     node->path = NULL;
654   }
655   node->next = flist->next;
656   flist->next = node;
657   return BT_ERROR_NONE;
658   }
659 }
660
661
662 /******************************************************************************
663 annotated file list data structure
664 the first in the list is a dummy
665 ******************************************************************************/
666 struct _bt_afile_list {
667   char *file;
668   bt_data path;
669   integer length;
670   bt_data md5sum;
671   struct _bt_afile_list *next;
672 };
673 typedef struct _bt_afile_list *bt_afile_list;
674
675 /******************************************************************************
676 create the dummy first list element
677 ******************************************************************************/
678 bt_afile_list bt_create_afile_list() {
679   bt_afile_list aflist = bt_malloc(sizeof(struct _bt_afile_list));
680   aflist->file = NULL;
681   aflist->path = NULL;
682   aflist->length = 0;
683   aflist->md5sum = NULL;
684   aflist->next = NULL;
685   return aflist;
686 }
687
688 /******************************************************************************
689 prepend to the annotated file list
690 aflist is an annotated file list to prepend to
691 fnode is a file list node to annotate
692 length is the length to annotate with
693 md5sum is the md5sum to annotate with (may be NULL)
694 NOTE does NOT create a new copy of the existing data
695 ******************************************************************************/
696 bt_error bt_afile_list_prepend(
697   bt_afile_list aflist, bt_file_list fnode, integer length, bt_data md5sum
698 ) {
699   if (!aflist) {
700     return BT_ERROR_NULL;
701   }
702   if (!fnode) {
703     return BT_ERROR_NULL;
704   }
705   {
706   bt_afile_list node = bt_malloc(sizeof(struct _bt_afile_list));
707   node->file = fnode->file;
708   node->path = fnode->path;
709   node->length = length;
710   node->md5sum = md5sum;
711   node->next = aflist->next;
712   aflist->next = node;
713   return BT_ERROR_NONE;
714   }
715 }
716
717
718 /******************************************************************************
719 append a file to a path string
720 path must be at least length maxlength bytes
721 returns an error flag
722 ******************************************************************************/
723 bt_error bt_joinpath(size_t maxlength, char *path, const char *file) {
724   if (strlen(path) + strlen(file) + 1 + 1 > maxlength) {
725     return BT_ERROR_OVERFLOW;
726   } else {
727     size_t oldlength = strlen(path);
728     if (oldlength > 0) {
729       path[oldlength] = '/';
730       memcpy(path + oldlength + 1, file, strlen(file) + 1);
731     } else {
732       memcpy(path, file, strlen(file) + 1);
733     }
734     return BT_ERROR_NONE;
735   }
736 }
737
738
739 /******************************************************************************
740 md5sum a file
741 ******************************************************************************/
742 bt_data bt_md5sum_file(const char *filename, integer length) {
743   struct MD5Context context;
744   char *hexdigits = "0123456789abcdef";
745   unsigned char *buffer = NULL;
746   unsigned char digest[16];
747   char hexdump[33];
748   integer buflen = 262144;
749   integer size = 0;
750   integer left = length;
751   integer i;
752   FILE *file = NULL;
753   if (!filename) {
754     return NULL;
755   }
756   buffer = bt_malloc(buflen);
757   if (!(file = fopen(filename, "rb"))) {
758     free(buffer);
759     return NULL;
760   }
761   MD5Init(&context);
762   while (left > 0) {
763     if (left > buflen) {
764        size = buflen;
765     } else {
766        size = left;
767     }
768     if (1 != fread(buffer, size, 1, file)) {
769       free(buffer);
770       fclose(file);
771       return NULL;
772     }
773     MD5Update(&context, buffer, size);
774     left -= size;
775   }
776   MD5Final(digest, &context);
777   for (i = 0; i < 16; ++i) {
778     hexdump[i+i+0] = hexdigits[(digest[i] & 0xF0) >> 4];
779     hexdump[i+i+1] = hexdigits[(digest[i] & 0x0F)];
780   }
781   free(buffer);
782   fclose(file);
783   return bt_string(32, hexdump);
784 }
785
786
787 /******************************************************************************
788 find files recursively
789 directory is the initial directory
790 path is a buffer of maxlength bytes to store the accumulated path
791 pathlist is the accumulated path exploded as a list
792 files is a file list to add the found files to
793 returns an error flag
794 ******************************************************************************/
795 bt_error bt_find_files(
796   char sort, size_t maxlength, char *path, bt_data pathlist, bt_file_list files
797 ) {
798   struct dirent **entry;
799   bt_data filename = NULL;
800   bt_data filepath = NULL;
801   bt_error err = BT_ERROR_NONE;
802   int i,n;
803
804   // replaced readdir with scandir, so we can get directory listings in alphabetical order
805   switch(sort)
806   {
807     default:
808 #ifdef _GNU_SOURCE
809     case 'v':
810       i = scandir(path, &entry, 0, versionsort); // read directory entry in "version" order, ie. 1 2 10, not 1 10 2
811       break;
812 #endif
813     case 'a':
814     case 's':
815       i = scandir(path, &entry, 0, alphasort); // read directory entry in "version" order, ie. 1 2 10, not 1 10 2
816       break;
817     case 'u':
818       i = scandir(path, &entry, 0, 0); // read directory entry normal order (directory order, seemingly random)
819       break;
820   }
821   if (i >=0 )  { // read directory entry in "version" order, ie. 1 2 10, not 1 10 2
822   for(n=0;n<i;n++) {
823     if (strcmp(entry[n]->d_name, ".") && strcmp(entry[n]->d_name, "..")) {
824       struct stat s;
825       size_t oldlength = strlen(path);
826       if (!(err = bt_joinpath(maxlength, path, entry[n]->d_name))) {
827         if (!stat(path, &s)) {
828           if (S_ISREG(s.st_mode)) {
829               filename = bt_string0(entry[n]->d_name);
830               if ((filepath = bt_copy(pathlist))) {
831                 if (!(err = bt_list_append(filepath, filename))) {
832                   err = bt_file_list_prepend(files, path, filepath);
833                 }
834               } else {
835                 err = BT_ERROR_MEMORY;
836               }
837             } else {
838               err = BT_ERROR_MEMORY;
839             }
840           } else if (S_ISDIR(s.st_mode)) {
841                 filename = bt_string0(entry[n]->d_name);
842                 if ((filepath = bt_copy(pathlist))) {
843                   if (!(err = bt_list_append(filepath, filename))) {
844                     err = bt_find_files(sort, maxlength, path, filepath, files);
845                   }
846                 } else {
847                   err = BT_ERROR_MEMORY;
848                 }
849               } else {
850                 err = BT_ERROR_MEMORY;
851               }
852           } else {
853             /* FIXME neither regular file nor directory, what to do? */
854           }
855         } else {
856           err = BT_ERROR_IO;
857         }
858       }
859       path[oldlength] = '\0';
860     }
861     if (err) {
862       break;
863     }
864   }
865   } else {
866     err = BT_ERROR_IO;
867   }
868   return err;
869 }
870
871
872 /******************************************************************************
873 annotate a file list
874 files is the file list to annotate
875 afiles is the file list to add to
876 returns an error flag
877 ******************************************************************************/
878 bt_error bt_annotate_files(
879   bt_file_list files, bt_afile_list afiles
880 ) {
881   bt_error err = BT_ERROR_NONE;
882   bt_file_list fnode = files;
883   while ((fnode = fnode->next)) {
884     struct stat s;
885     if (!stat(fnode->file, &s)) {
886       if (S_ISREG(s.st_mode)) {
887         integer length = s.st_size;
888         err = bt_afile_list_prepend(afiles, fnode, length, NULL);
889       } else {
890         err = BT_ERROR_IO;
891       }
892     } else {
893       err = BT_ERROR_IO;
894     }
895     if (err) {
896       break;
897     }
898   }
899   return err;
900 }
901
902
903 /******************************************************************************
904 convert an annotated file list into torrent format
905 aflist is the list to convert
906 returns the torrent format file list
907 ******************************************************************************/
908 bt_data bt_afile_list_info(bt_afile_list aflist) {
909   bt_afile_list node;
910   bt_data files;
911   if (!(files = bt_list())) {
912     return NULL;
913   }
914   node = aflist;
915   while ((node = node->next)) {
916     bt_data file;
917     bt_data filesize;
918     bt_data filepath;
919     bt_data md5sum;
920     file = bt_dictionary();
921     if (!(filesize = bt_integer(node->length))) {
922       return NULL;
923     }
924     if (!(filepath = node->path)) {
925       return NULL;
926     }
927     if (bt_dictionary_insert0(file, "length", filesize)) {
928       return NULL;
929     }
930     if (bt_dictionary_insert0(file, "path", filepath)) {
931       return NULL;
932     }
933     if ((md5sum = node->md5sum)) {
934       if (bt_dictionary_insert0(file, "md5sum", md5sum)) {
935         return NULL;
936       }
937     }
938     if (bt_list_append(files, file)) {
939       return NULL;
940     }
941   }
942   return files;
943 }
944
945
946 /******************************************************************************
947 draw progressbar
948 ******************************************************************************/
949 void bt_progressbar(integer piece, integer count) {
950   integer oldblocks = ((piece - 1) * 50) / count;
951   integer blocks = (piece * 50) / count;
952   integer i;
953   char s[53];
954   if (blocks != oldblocks) {
955     s[0] = '[';
956     for (i = 0; i < 50; i++) {
957       s[i+1] = (i < blocks) ? '=' : ((i == blocks) ? '>' : ' ');
958     }
959     s[51] = ']';
960     s[52] = '\0';
961     fprintf(
962       stderr,
963       "\b\b\b\b\b\b\b\b\b\b"
964       "\b\b\b\b\b\b\b\b\b\b"
965       "\b\b\b\b\b\b\b\b\b\b"
966       "\b\b\b\b\b\b\b\b\b\b"
967       "\b\b\b\b\b\b\b\b\b\b"
968       "\b\b%s",
969       s
970     );
971   }
972 }
973
974
975 /******************************************************************************
976 hash files
977 return piece hash as a string
978 data side effect: if domd5sum then add md5sums to aflist
979 output side effect: if verbose then show file summary
980 ******************************************************************************/
981 bt_data bt_hash_pieces(bt_afile_list aflist, integer size, int domd5sum, int verbose) {
982   bt_afile_list node = NULL;
983   char *hashdata = NULL;
984   integer total = 0;
985   unsigned char *buffer = NULL;
986   unsigned char *bufptr = NULL;
987   integer remain = size;
988   integer left;
989   FILE *file = NULL;
990   integer piececount;
991   integer i;
992   sha1_byte digest[SHA1_DIGEST_LENGTH];
993   if (!aflist) {
994     return NULL;
995   }
996   buffer = bt_malloc(size);
997   node = aflist;
998   while ((node = node->next)) {
999     if (verbose) {
1000       fprintf(stderr, "%20" PRId64 " : %s\n", node->length, node->file);
1001     }
1002     if (domd5sum) {
1003       if (!(node->md5sum = bt_md5sum_file(node->file, node->length))) {
1004         fprintf(stderr, "buildtorrent: error computing md5sum for \"%s\"\n", node->file);
1005       }
1006     }
1007     total += node->length;
1008   }
1009   piececount = (total + size - 1) / size; /* ceil(total/size) */
1010   if (piececount <= 0) { /* FIXME: no idea what to do if there's no data */
1011     free(buffer);
1012     fprintf(stderr, "torrent has no data, aborting!\n");
1013     return NULL;
1014   }
1015   if (verbose) {
1016     fprintf(stderr, "hashing %" PRId64 " pieces\n", piececount);
1017     fprintf(stderr, "["
1018                     "          "
1019                     "          "
1020                     "          "
1021                     "          "
1022                     "          "
1023                     "]");
1024   }
1025   hashdata = bt_malloc(piececount * SHA1_DIGEST_LENGTH);
1026   node = aflist->next;
1027   file = fopen(node->file, "rb");
1028   if (!file) {
1029     free(buffer);
1030     return NULL;
1031   }
1032   left = node->length;
1033   bufptr = buffer;
1034   for (i = 0; i < piececount; ++i) {
1035     do {
1036       if (left <= remain) {
1037         /* take all */
1038         if (left != 0) { /* don't fail on empty files */
1039           if (1 != fread(bufptr, left, 1, file)) {
1040             fclose(file);
1041             free(buffer);
1042             return NULL;
1043           }
1044           bufptr += left;
1045           remain -= left;
1046           fclose(file);
1047           file = NULL;
1048         }
1049         node = node->next;
1050         if (node) {
1051           file = fopen(node->file, "rb");
1052           if (!file) {
1053             free(buffer);
1054             return NULL;
1055           }
1056           left = node->length;
1057         }
1058       } else { /* left > remain */
1059         /* take as much as we can */
1060         if (remain != 0) { /* don't fail on empty files */
1061           if (1 != fread(bufptr, remain, 1, file)) {
1062             free(buffer);
1063             return NULL;
1064           }
1065           bufptr += remain;
1066           left -= remain;
1067           remain = 0;
1068         }
1069       }
1070     } while (remain != 0 && node);
1071     if (!node && i != piececount - 1) {
1072       /* somehow the pieces don't add up */
1073       if (file) {
1074         fclose(file);
1075       }
1076       free(buffer);
1077       return NULL;
1078     }
1079     /* remain == 0 || i == piececount - 1 */
1080     if (verbose) {
1081       bt_progressbar(i, piececount);
1082     }
1083     SHA1(buffer, size - remain, digest);
1084     memcpy(hashdata + i * SHA1_DIGEST_LENGTH, digest, SHA1_DIGEST_LENGTH);
1085     bufptr = buffer;
1086     remain = size;
1087   }
1088   if (verbose) {
1089     bt_progressbar(piececount, piececount);
1090     fprintf(stderr, "\n");
1091   }
1092   return bt_string(SHA1_DIGEST_LENGTH * piececount, hashdata);
1093 }
1094
1095
1096 /******************************************************************************
1097 parse an announce list
1098 format = "url|url|url,url|url|url,url|url|url"
1099 ******************************************************************************/
1100 bt_data bt_parse_announcelist(const char *urls) {
1101   bt_data announcelist;
1102   bt_data tier;
1103   const char *s;
1104   const char *t;
1105   const char *t1;
1106   const char *t2;
1107   if (!urls) {
1108     return NULL;
1109   }
1110   if (strcmp("", urls) == 0) {
1111     return NULL;
1112   }
1113   announcelist = bt_list();
1114   if (!announcelist) {
1115     return NULL;
1116   }
1117   s = urls;
1118   tier = bt_list();
1119   do {
1120     t = NULL;
1121     t1 = strchr(s, '|');
1122     t2 = strchr(s, ',');
1123     if (!t1 && !t2) {
1124       t = s + strlen(s);
1125     } else if (!t1) {
1126       t = t2;
1127     } else if (!t2) {
1128       t = t1;
1129     } else {
1130       t = (t1 < t2) ? t1 : t2;
1131     }
1132     if (t <= s) {
1133       return NULL;
1134     }
1135     if (bt_list_append(tier, bt_string(t - s, s))) {
1136       return NULL;
1137     }
1138     if (t[0] == ',' || t[0] == '\0') {
1139       if (bt_list_append(announcelist, tier)) {
1140         return NULL;
1141       };
1142       if (t[0] != '\0') {
1143         tier = bt_list();
1144       } else {
1145         tier = NULL;
1146       }
1147     }
1148     s = t + 1;
1149   } while (t[0] != '\0');
1150   return announcelist;
1151 }
1152
1153 /******************************************************************************
1154 parse a webseed list
1155 format = "url,url,url"
1156 ******************************************************************************/
1157 bt_data bt_parse_webseedlist(const char *urls) {
1158   bt_data webseedlist;
1159   const char *s;
1160   const char *t;
1161   const char *t2;
1162   if (!urls) {
1163     return NULL;
1164   }
1165   if (strcmp("", urls) == 0) {
1166     return NULL;
1167   }
1168   webseedlist = bt_list();
1169   if (!webseedlist) {
1170     return NULL;
1171   }
1172   s = urls;
1173   do {
1174     t = NULL;
1175     t2 = strchr(s, ',');
1176     if (!t2) {
1177       t = s + strlen(s);
1178     } else {
1179       t = t2;
1180     }
1181     if (t <= s) {
1182       return NULL;
1183     }
1184     if (bt_list_append(webseedlist, bt_string(t - s, s))) {
1185       return NULL;
1186     }
1187     s = t + 1;
1188   } while (t[0] != '\0');
1189   return webseedlist;
1190 }
1191
1192 /******************************************************************************
1193 read and parse a filelist file
1194 line format = "real/file/system/path|tor/rent/path/file"
1195 delimiters: '|', '\n'
1196 escape character: '\\'
1197 ******************************************************************************/
1198 enum _bt_parse_state {
1199   BT_PARSE_LEFT = 0,
1200   BT_PARSE_LEFT_ESCAPED,
1201   BT_PARSE_RIGHT,
1202   BT_PARSE_RIGHT_ESCAPED,
1203   BT_PARSE_OK,
1204   BT_PARSE_ERROR
1205 };
1206 typedef enum _bt_parse_state bt_parse_state;
1207 bt_error bt_parse_filelist(bt_file_list flist, FILE *infile) {
1208   char filename[8192];
1209   char torrname[8192];
1210   bt_data torrpath = NULL;
1211   int c;
1212   int i = 0;
1213   int state = BT_PARSE_LEFT;
1214   while(state != BT_PARSE_OK && state != BT_PARSE_ERROR) {
1215     c = getc(infile);
1216     switch (state) {
1217     case (BT_PARSE_LEFT):
1218       if (c < 0) {
1219         state = BT_PARSE_OK;
1220       } else if (c == '\\') {
1221         state = BT_PARSE_LEFT_ESCAPED;
1222       } else if (c == '|') {
1223         filename[i] = '\0';
1224         i = 0;
1225         torrpath = bt_list();
1226         if (!torrpath) {
1227           state = BT_PARSE_ERROR;
1228         } else {
1229           state = BT_PARSE_RIGHT;
1230         }
1231       } else {
1232         filename[i++] = c;
1233         if (i > 8190) {
1234           state = BT_PARSE_ERROR;
1235         } else {
1236           state = BT_PARSE_LEFT;
1237         }
1238       }
1239       break;
1240     case (BT_PARSE_LEFT_ESCAPED):
1241       if (c < 0) {
1242         state = BT_PARSE_ERROR;
1243       } else {
1244         filename[i++] = c;
1245         if (i > 8190) {
1246           state = BT_PARSE_ERROR;
1247         } else {
1248           state = BT_PARSE_LEFT;
1249         }
1250       }
1251       break;
1252     case (BT_PARSE_RIGHT):
1253       if (c < 0) {
1254         state = BT_PARSE_ERROR;
1255       } else if (c == '\\') {
1256         state = BT_PARSE_RIGHT_ESCAPED;
1257       } else if (c == '/' || c == '\n') {
1258         bt_data torrnamestr;
1259         torrname[i] = '\0';
1260         i = 0;
1261         if (0 == strcmp("", torrname)) {
1262           state = BT_PARSE_ERROR;
1263         } else {
1264           torrnamestr = bt_string0(torrname);
1265           bt_list_append(torrpath, torrnamestr);
1266           if (c == '\n') {
1267             bt_file_list_prepend(flist, filename, torrpath);
1268             state = BT_PARSE_LEFT;
1269           } else {
1270             state = BT_PARSE_RIGHT;
1271           }
1272         }
1273       } else {
1274         torrname[i++] = c;
1275         if (i > 8190) {
1276           state = BT_PARSE_ERROR;
1277         } else {
1278           state = BT_PARSE_RIGHT;
1279         }
1280       }
1281       break;
1282     case (BT_PARSE_RIGHT_ESCAPED):
1283       if (c < 0) {
1284         state = BT_PARSE_ERROR;
1285       } else {
1286         torrname[i++] = c;
1287         if (i > 8190) {
1288           state = BT_PARSE_ERROR;
1289         } else {
1290           state = BT_PARSE_RIGHT;
1291         }
1292       }
1293       break;
1294     default:
1295       fprintf(stderr, "buildtorrent: internal error parsing file list (%d)\n", state);
1296       break;
1297     }
1298   }
1299   if (state == BT_PARSE_OK) {
1300     return BT_ERROR_NONE;
1301   } else {
1302     return BT_ERROR_PARSE;
1303   }
1304 }
1305
1306 /******************************************************************************
1307 show usage message
1308 ******************************************************************************/
1309 void bt_usage(void) {
1310   printf(
1311     "Usage:\n"
1312     "  buildtorrent [OPTIONS] -a announceurl input output\n"
1313     "  buildtorrent [OPTIONS] -a announceurl -f filelist -n name output\n"
1314     "\n"
1315     "options:\n"
1316     "--announce        -a  <announce>   : announce url (required)\n"
1317     "--filelist        -f  <filelist>   : external file list (requires '-n')\n"
1318     "--name            -n  <name>       : torrent name, default based on input\n"
1319     "--announcelist    -A  <announces>  : announce url list (format: a,b1|b2,c)\n"
1320     "--webseeds        -w  <webseeds>   : webseed url list (format: a,b,c)\n"
1321     "--piecelength     -l  <length>     : piece length in bytes, default 262144\n"
1322     "--piecesize       -L  <size>       : use 2^size as piece length, default 18\n"
1323     "--comment         -c  <comment>    : user comment, omitted by default\n"
1324     "--private         -p  <private>    : private flag, either 0 or 1\n"
1325     "--nodate          -D               : omit 'creation date' field\n"
1326     "--nocreator       -C               : omit 'created by' field\n"
1327     "--md5sum          -m               : add an 'md5sum' field for each file\n"
1328 //    "--show            -s               : show generated torrent structure\n"
1329 //    "--showall         -S               : show pieces too (implies '-s')\n"
1330     "--sort            -s <sortorder>   : sort files ([U]nsorted, "
1331 #ifdef _GNU_SOURCE
1332     "[A]lpha, [V]ersion)\n"
1333 #else
1334     "[A]lpha)\n"
1335 #endif
1336     "--quiet           -q               : quiet operation\n"
1337     "--verbose         -v               : verbose. More -v means more verbose\n"
1338     "--version         -V               : show version of buildtorrent\n"
1339     "--help            -h               : show this help screen\n"
1340   );
1341 }
1342
1343 /******************************************************************************
1344 main program
1345 ******************************************************************************/
1346 int main(int argc, char **argv) {
1347
1348   char *url = NULL;
1349   char *urls = NULL;
1350   char *wurls = NULL;
1351   char *inname = NULL;
1352   char *nameflag = NULL;
1353   char *namebase = NULL;
1354   char *outfile = NULL;
1355   char *commentstr = NULL;
1356   char *filelistfilename = NULL;
1357 #ifdef _GNU_SOURCE
1358   char sort = 'v'; // default to version sort, if available
1359 #else
1360   char sort = 'a'; // otherwise default to alpha sort
1361 #endif
1362   int lplen = -1;
1363   unsigned int plen = 262144;
1364   int verbose = 1;
1365   int nodate = 0;
1366   int nocreator = 0;
1367   int privated = 0;
1368   int privateopt = 0;
1369   int domd5sum = 0;
1370   int show = 0;
1371   int slen;
1372   int i;
1373
1374   FILE *output = NULL;
1375   bt_data torrent = NULL;
1376   bt_data announce = NULL;
1377   bt_data announcelist = NULL;
1378   bt_data webseedlist = NULL;
1379   bt_data info = NULL;
1380   bt_data piecelength = NULL;
1381   bt_data pieces = NULL;
1382   bt_data files = NULL;
1383   bt_data name = NULL;
1384   bt_data length = NULL;
1385   bt_data pathlist = NULL;
1386   bt_data creator = NULL;
1387   bt_data creationdate = NULL;
1388   bt_data comment = NULL;
1389   bt_data private = NULL;
1390   bt_data md5sum = NULL;
1391
1392   int multifile = 0;
1393   bt_file_list flist = NULL;
1394   bt_afile_list aflist = NULL;
1395
1396   struct stat s;
1397   char path[8192];
1398   char nametemp[8192];
1399
1400   while (1) {
1401     int optidx = 0;
1402     static struct option options[] = {
1403       { "announce", 1, 0, 'a' },
1404       { "filelist", 1, 0, 'f' },
1405       { "name", 1, 0, 'n' },
1406       { "announcelist", 1, 0, 'A' },
1407       { "webseeds", 1, 0, 'w' },
1408       { "piecelength", 1, 0, 'l' },
1409       { "piecesize", 1, 0, 'L' },
1410       { "comment", 1, 0, 'c' },
1411       { "private", 1, 0, 'p' },
1412       { "sort", 1, 0, 's' },
1413       { "nodate", 0, 0, 'D' },
1414       { "nocreator", 0, 0, 'C' },
1415       { "md5sum", 0, 0, 'm' },
1416 //      { "show", 0, 0, 's' },
1417 //      { "showpieces", 0, 0, 'S' },
1418       { "quiet", 0, 0, 'q' },
1419       { "verbose", 0, 0, 'v' },
1420       { "version", 0, 0, 'V' },
1421       { "help", 0, 0, 'h' },
1422       { 0, 0, 0, 0 }
1423     };
1424     char c = getopt_long(argc, argv, "hVqvmCDs:a:f:n:A:w:l:L:c:p:", options, &optidx );
1425     if (c == -1) {
1426       break;
1427     }
1428     switch (c) {
1429     case ('?'):
1430       return 1;
1431     case ('a'):
1432       url = optarg;
1433       break;
1434     case ('f'):
1435       filelistfilename = optarg;
1436       break;
1437     case ('n'):
1438       nameflag = optarg;
1439       break;
1440     case ('A'):
1441       urls = optarg;
1442       break;
1443     case ('w'):
1444       wurls = optarg;
1445       break;
1446     case ('l'):
1447       plen = atoi(optarg);
1448       break;
1449     case ('L'):
1450       lplen = atoi(optarg);
1451       break;
1452     case ('c'):
1453       commentstr = optarg;
1454       break;
1455     case ('p'):
1456       privated = 1;
1457       privateopt = (strcmp(optarg, "0") == 0) ? 0 : 1;
1458       break;
1459     case ('s'):
1460       sort = tolower(optarg[0]);
1461       break;
1462     case ('D'):
1463       nodate = 1;
1464       break;
1465     case ('C'):
1466       nocreator = 1;
1467       break;
1468     case ('m'):
1469       domd5sum = 1;
1470       break;
1471     case ('v'):
1472       show++;
1473       if(show > 2)
1474         show=2;
1475       break;
1476     case ('q'):
1477       verbose = 0;
1478       break;
1479     case ('V'):
1480       printf(
1481         "buildtorrent " bt_version "\n"
1482         "Copyright (C) 2007-2010 Claude Heiland-Allen <claudiusmaximus@goto10.org>\n"
1483         "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
1484       );
1485       return 0;
1486     case ('h'):
1487       bt_usage();
1488       return 0;
1489     }
1490   }
1491   if (!url) {
1492     fprintf(stderr, "buildtorrent: announce url required\n");
1493     return 1;
1494   }
1495   if (0 <= lplen && lplen < 31) {
1496     plen = 1 << lplen;
1497   }
1498   if (plen <= 0) { /* avoid division by zero */
1499     fprintf(stderr, "buildtorrent: piece length must be greater than 0\n");
1500     return 1;
1501   }
1502
1503   if (filelistfilename) {
1504     if (optind + 1 < argc) {
1505       fprintf(stderr, "buildtorrent: too many arguments\n");
1506       return 1;
1507     }
1508     if (optind + 1 > argc) {
1509       fprintf(stderr, "buildtorrent: too few arguments\n");
1510       return 1;
1511     }
1512     if (!nameflag) {
1513       fprintf(stderr, "buildtorrent: missing '-n', required when using '-f'\n");
1514       return 1;
1515     }
1516     inname = NULL;
1517     outfile = argv[optind];
1518   } else {
1519     if (optind + 2 < argc) {
1520       fprintf(stderr, "buildtorrent: too many arguments\n");
1521       return 1;
1522     }
1523     if (optind + 2 > argc) {
1524       fprintf(stderr, "buildtorrent: too few arguments\n");
1525       return 1;
1526     }
1527     inname  = argv[optind];
1528     outfile = argv[optind + 1];
1529   }
1530
1531   /* handle paths correctly (note: requires POSIX basename(), not GNU) */
1532   if (inname) {
1533     if (strlen(inname) > 8190) {
1534       fprintf(stderr, "buildtorrent: 'input' argument too long\n");
1535       return 1;
1536     }
1537     strncpy(nametemp, inname, 8191);
1538     nametemp[8191] = '\0';
1539     namebase = basename(nametemp);
1540     slen = strlen(namebase);
1541     for (i = 0; i < slen; ++i) {
1542       if (namebase[i] == '/') {
1543         fprintf(
1544           stderr,
1545           "buildtorrent: BUG! input (\"%s\") munged (\"%s\") contains '/'.\n",
1546           inname,
1547           namebase
1548         );
1549         return 1;
1550       }
1551     }
1552   }
1553
1554   if (inname) {
1555     if (stat(inname, &s)) {
1556       fprintf(stderr, "buildtorrent: could not stat \"%s\"\n", inname);
1557       return 1;
1558     }
1559   }
1560
1561   torrent = bt_dictionary();
1562   info = bt_dictionary();
1563   announce = bt_string0(url);
1564   if (!(piecelength = bt_integer(plen))) {
1565     fprintf(stderr, "buildtorrent: couldn't allocate piece length integer\n");
1566     return 1;
1567   }
1568   if (nameflag) {
1569     name = bt_string0(nameflag);
1570   } else {
1571     name = bt_string0(namebase);
1572   }
1573   if (bt_dictionary_insert0(info, "name", name)) {
1574     fprintf(stderr, "buildtorrent: couldn't insert name into info\n");
1575     return 1;
1576   }
1577   if (bt_dictionary_insert0(info, "piece length", piecelength)) {
1578     fprintf(stderr, "buildtorrent: couldn't insert piece length into info\n");
1579     return 1;
1580   }
1581   if (urls) {
1582     if (!(announcelist = bt_parse_announcelist(urls))) {
1583       fprintf(stderr, "buildtorrent: error parsing announce-list argument\n");
1584       return 1;
1585     }
1586   }
1587   if (wurls) {
1588     if (!(webseedlist = bt_parse_webseedlist(wurls))) {
1589       fprintf(stderr, "buildtorrent: error parsing webseed list argument\n");
1590       return 1;
1591     }
1592   }
1593   if (!(flist = bt_create_file_list())) {
1594     fprintf(stderr, "buildtorrent: couldn't allocate file list\n");
1595     return 1;
1596   }
1597   if (!(aflist = bt_create_afile_list())) {
1598     fprintf(stderr, "buildtorrent: couldn't allocate annotated file list\n");
1599     return 1;
1600   }
1601
1602   if (inname && S_ISDIR(s.st_mode)) {
1603
1604     multifile = 1;
1605     if (!(pathlist = bt_list())) {
1606       fprintf(stderr, "buildtorrent: couldn't path list\n");
1607       return 1;
1608     }
1609     memcpy(path, inname, strlen(inname) + 1);
1610     if (bt_find_files(sort, 8192, path, pathlist, flist)) {
1611       fprintf(stderr, "buildtorrent: error finding files\n");
1612       return 1;
1613     }
1614
1615   } else if (inname && S_ISREG(s.st_mode)) {
1616
1617     multifile = 0;
1618     if ((bt_file_list_prepend(flist, inname, NULL))) {
1619       fprintf(stderr, "buildtorrent: error building single file list\n");
1620       return 1;
1621     }
1622
1623   } else if (!inname) {
1624  
1625     multifile = 1;
1626     bt_error err = BT_ERROR_NONE;
1627     if (0 == strcmp("-", filelistfilename)) {
1628       err = bt_parse_filelist(flist, stdin);
1629     } else {
1630       FILE *filelistfile;
1631       if ((filelistfile = fopen(filelistfilename, "rb"))) {
1632         err = bt_parse_filelist(flist, filelistfile);
1633         fclose(filelistfile);
1634       } else {
1635         fprintf(stderr, "buildtorrent: couldn't open file list \"%s\"\n", filelistfilename);
1636         return 1;        
1637       }
1638     }
1639     if (err) {
1640       fprintf(stderr, "buildtorrent: error processing file list\n");
1641       return 1;
1642     }
1643
1644   } else {
1645
1646     fprintf(
1647       stderr, "buildtorrent: \"%s\" is neither file nor directory\n", inname
1648     );
1649     return 1;
1650
1651   }
1652
1653   if ((bt_annotate_files(flist, aflist))) {
1654     fprintf(stderr, "buildtorrent: error annotating file list\n");
1655     return 1;
1656   }
1657
1658   if (privated) {
1659     if (!(private = bt_integer(privateopt))) {
1660       fprintf(stderr, "buildtorrent: couldn't allocate private integer\n");
1661       return 1;
1662     }
1663     if (bt_dictionary_insert0(info, "private", private)) {
1664       fprintf(stderr, "buildtorrent: couldn't insert private into info\n");
1665       return 1;
1666     }
1667   }  
1668   if (bt_dictionary_insert0(torrent, "announce", announce)) {
1669     fprintf(stderr, "buildtorrent: couldn't insert announce into torrent\n");
1670     return 1;
1671   }
1672   if (urls) {
1673     if (bt_dictionary_insert0(torrent, "announce-list", announcelist)) {
1674       fprintf(stderr, "buildtorrent: couldn't insert announce-list into torrent\n");
1675       return 1;
1676     }
1677   }
1678   if (wurls) {
1679     if (bt_dictionary_insert0(torrent, "url-list", webseedlist)) {
1680       fprintf(stderr, "buildtorrent: couldn't insert webseed url-list into torrent\n");
1681       return 1;
1682     }
1683   }
1684   if (!nodate) {
1685     if (!(creationdate = bt_integer(time(NULL)))) {
1686       fprintf(stderr, "buildtorrent: couldn't allocate creation date integer\n");
1687       return 1;
1688     }
1689     if (bt_dictionary_insert0(torrent, "creation date", creationdate)) {
1690       fprintf(stderr, "buildtorrent: couldn't insert creation date into torrent\n");
1691       return 1;
1692     }
1693   }
1694   if (!nocreator) {
1695     creator = bt_string0("buildtorrent/" bt_version);
1696     if (bt_dictionary_insert0(torrent, "created by", creator)) {
1697       fprintf(stderr, "buildtorrent: couldn't insert created by into torrent\n");
1698       return 1;
1699     }
1700   }
1701   if (commentstr) {
1702     comment = bt_string0(commentstr);
1703     if (bt_dictionary_insert0(torrent, "comment", comment)) {
1704       fprintf(stderr, "buildtorrent: couldn't insert comment into torrent\n");
1705       return 1;
1706     }
1707   }
1708   if (!(output = fopen(outfile, "wb"))) {
1709     fprintf(stderr, "buildtorrent: couldn't open \"%s\" for writing\n", outfile);
1710     return 1;
1711   }
1712
1713   if (!(pieces = bt_hash_pieces(aflist, plen, domd5sum, verbose))) {
1714     fprintf(stderr, "buildtorrent: error hashing files\n");
1715     return 1;
1716   }
1717
1718   if (multifile) {
1719     if (!(files = bt_afile_list_info(aflist))) {
1720       fprintf(stderr, "buildtorrent: error getting file list info\n");
1721       return 1;
1722     }
1723     if (bt_dictionary_insert0(info, "files", files)) {
1724       fprintf(stderr, "buildtorrent: couldn't insert files into info\n");
1725       return 1;
1726     }
1727   } else {
1728     bt_afile_list node = aflist->next;
1729     if (!(length = bt_integer(node->length))) {
1730       fprintf(stderr, "buildtorrent: couldn't allocate length integer\n");
1731       return 1;
1732     }
1733     if (bt_dictionary_insert0(info, "length", length)) {
1734       fprintf(stderr, "buildtorrent: couldn't insert length into info\n");
1735       return 1;
1736     }
1737     if (node->md5sum) {
1738       if (bt_dictionary_insert0(info, "md5sum", node->md5sum)) {
1739         fprintf(stderr, "buildtorrent: couldn't insert md5sum into info\n");
1740         return 1;
1741       }
1742     }
1743   }
1744
1745   if (bt_dictionary_insert0(info, "pieces", pieces)) {
1746     fprintf(stderr, "buildtorrent: couldn't insert pieces into info\n");
1747     return 1;
1748   }
1749   if (bt_dictionary_insert0(torrent, "info", info)) {
1750     fprintf(stderr, "buildtorrent: couldn't insert info into torrent\n");
1751     return 1;
1752   }
1753
1754   if (bt_write(output, torrent)) {
1755     fprintf(stderr, "buildtorrent: error writing \"%s\"\n", outfile);
1756     return 1;
1757   }
1758   if (show) {
1759     printf("torrent =>\n");
1760     bt_show(torrent, show == 2, 2, 2, 0);
1761   }
1762   bt_free(torrent);
1763   fclose(output);
1764   return 0;
1765 }
1766
1767 /* EOF */