ScoTTie <scott@wizzer-it.com>/erie-- <seditaniedi@gmail.com>
[reactos.git] / reactos / tools / cdmake / cdmake.c
1 /*
2 * CD-ROM Maker
3 * by Philip J. Erdelsky
4 * pje@acm.org
5 * http://www.alumni.caltech.edu/~pje/
6 *
7 * ElTorito-Support
8 * by Eric Kohl
9 * ekohl@rz-online.de
10 *
11 * Linux port
12 * by Casper S. Hornstrup
13 * chorns@users.sourceforge.net
14 *
15 * Joliet support
16 * by Filip Navara
17 * xnavara@volny.cz
18 * Limitations:
19 * - No Joliet file name validations
20 * - Very bad ISO file name generation
21 *
22 *
23 * convert long filename to iso9660 file name by Magnus Olsen
24 * magnus@greatlord.com
25 *
26 * $Id$
27 */
28
29 /* According to his website, this file was released into the public domain by Phillip J. Erdelsky */
30
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #ifdef WIN32
38 # include <io.h>
39 # include <dos.h>
40 #else
41 # ifdef __FreeBSD__
42 # include <sys/uio.h>
43 # else
44 # include <sys/io.h>
45 # endif // __FreeBSD__
46 # include <errno.h>
47 # include <sys/types.h>
48 # include <dirent.h>
49 # include <unistd.h>
50 #endif // WIN32
51 #include <ctype.h>
52 #include <setjmp.h>
53 #include <time.h>
54 #ifndef WIN32
55 #ifndef MAX_PATH
56 #define MAX_PATH 260
57 #endif
58 #define DIR_SEPARATOR_CHAR '/'
59 #define DIR_SEPARATOR_STRING "/"
60 #else
61 #define DIR_SEPARATOR_CHAR '\\'
62 #define DIR_SEPARATOR_STRING "\\"
63 #endif
64
65 typedef unsigned char BYTE;
66 typedef unsigned short WORD;
67 typedef unsigned long DWORD;
68 typedef int BOOL;
69
70 const BOOL TRUE = 1;
71 const BOOL FALSE = 0;
72
73 // file system parameters
74
75 #define MAX_LEVEL 8
76 #define MAX_NAME_LENGTH 64
77 #define MAX_CDNAME_LENGTH 8
78 #define MAX_EXTENSION_LENGTH 10
79 #define MAX_CDEXTENSION_LENGTH 3
80 #define SECTOR_SIZE 2048
81 #define BUFFER_SIZE (8 * SECTOR_SIZE)
82
83 const BYTE HIDDEN_FLAG = 1;
84 const BYTE DIRECTORY_FLAG = 2;
85
86
87 struct cd_image
88 {
89 FILE *file;
90 DWORD sector; // sector to receive next byte
91 int offset; // offset of next byte in sector
92 int count; // number of bytes in buffer
93 char filespecs[128];
94 BYTE *buffer;
95 };
96
97 typedef struct date_and_time
98 {
99 BYTE second;
100 BYTE minute;
101 BYTE hour;
102 BYTE day;
103 BYTE month;
104 WORD year;
105 } DATE_AND_TIME, *PDATE_AND_TIME;
106
107 typedef struct directory_record
108 {
109 struct directory_record *next_in_directory;
110 struct directory_record *next_in_path_table; /* directory record only */
111 struct directory_record *next_in_memory;
112 struct directory_record *first_record; /* directory record only */
113 struct directory_record *parent;
114 BYTE flags;
115 char name[MAX_NAME_LENGTH+1];
116 char name_on_cd[MAX_CDNAME_LENGTH+1];
117 char extension[MAX_EXTENSION_LENGTH+1];
118 char extension_on_cd[MAX_CDEXTENSION_LENGTH+1];
119 char *joliet_name;
120 DATE_AND_TIME date_and_time;
121 DWORD sector;
122 DWORD size;
123 DWORD joliet_sector;
124 DWORD joliet_size;
125 unsigned level; /* directory record only */
126 WORD path_table_index; /* directory record only */
127 } DIR_RECORD, *PDIR_RECORD;
128
129
130 typedef enum directory_record_type
131 {
132 DOT_RECORD,
133 DOT_DOT_RECORD,
134 SUBDIRECTORY_RECORD,
135 FILE_RECORD
136 } DIR_RECORD_TYPE, *PDIR_RECORD_TYPE;
137
138
139 PDIR_RECORD sort_linked_list(PDIR_RECORD,
140 unsigned, int (*)(PDIR_RECORD, PDIR_RECORD));
141
142
143 static char DIRECTORY_TIMESTAMP[] = "~Y$'KOR$.3K&";
144
145 static jmp_buf error;
146 static struct cd_image cd;
147
148 char volume_label[32];
149 DIR_RECORD root;
150 char source[512];
151 char *end_source;
152 enum {QUIET, NORMAL, VERBOSE} verbosity;
153 BOOL show_progress;
154 DWORD size_limit;
155 BOOL accept_punctuation_marks;
156
157 DWORD total_sectors;
158 DWORD path_table_size;
159 DWORD little_endian_path_table_sector;
160 DWORD big_endian_path_table_sector;
161 DWORD number_of_files;
162 DWORD bytes_in_files;
163 DWORD unused_bytes_at_ends_of_files;
164 DWORD number_of_directories;
165 DWORD bytes_in_directories;
166
167 char bootimage[512];
168 BOOL eltorito;
169 DWORD boot_catalog_sector;
170 DWORD boot_image_sector;
171 WORD boot_image_size; // counted in 512 byte sectors
172
173 BOOL joliet;
174 DWORD joliet_path_table_size;
175 DWORD joliet_little_endian_path_table_sector;
176 DWORD joliet_big_endian_path_table_sector;
177
178 /*-----------------------------------------------------------------------------
179 This function edits a 32-bit unsigned number into a comma-delimited form, such
180 as 4,294,967,295 for the largest possible number, and returns a pointer to a
181 static buffer containing the result. It suppresses leading zeros and commas,
182 but optionally pads the result with blanks at the left so the result is always
183 exactly 13 characters long (excluding the terminating zero).
184
185 CAUTION: A statement containing more than one call on this function, such as
186 printf("%s, %s", edit_with_commas(a), edit_with_commas(b)), will produce
187 incorrect results because all calls use the same static bufffer.
188 -----------------------------------------------------------------------------*/
189
190 static char *edit_with_commas(DWORD x, BOOL pad)
191 {
192 static char s[14];
193 unsigned i = 13;
194 do
195 {
196 if (i % 4 == 2) s[--i] = ',';
197 s[--i] = (char)(x % 10 + '0');
198 } while ((x/=10) != 0);
199 if (pad)
200 {
201 while (i > 0) s[--i] = ' ';
202 }
203 return s + i;
204 }
205
206 /*-----------------------------------------------------------------------------
207 This function releases all allocated memory blocks.
208 -----------------------------------------------------------------------------*/
209
210 static void release_memory(void)
211 {
212 while (root.next_in_memory != NULL)
213 {
214 struct directory_record *next =
215 root.next_in_memory->next_in_memory;
216 if (joliet)
217 free (root.joliet_name);
218 free (root.next_in_memory);
219 root.next_in_memory = next;
220 }
221 if (cd.buffer != NULL)
222 {
223 free (cd.buffer);
224 cd.buffer = NULL;
225 }
226 }
227
228 /*-----------------------------------------------------------------------------
229 This function edits and displays an error message and then jumps back to the
230 error exit point in main().
231 -----------------------------------------------------------------------------*/
232
233 void error_exit ( const char* fmt, ... )
234 {
235 va_list arg;
236
237 va_start(arg, fmt);
238 vprintf(fmt, arg);
239 va_end(arg);
240 printf("\n");
241 if (cd.file != NULL)
242 fclose(cd.file);
243 release_memory();
244 exit(1);
245 }
246
247 /*-----------------------------------------------------------------------------
248 This function, which is called only on the second pass, and only when the
249 buffer is not empty, flushes the buffer to the CD-ROM image.
250 -----------------------------------------------------------------------------*/
251
252 static void flush_buffer(void)
253 {
254 if (fwrite(cd.buffer, cd.count, 1, cd.file) < 1)
255 error_exit("File write error");
256 cd.count = 0;
257 if (show_progress)
258 {
259 printf("\r%s ",
260 edit_with_commas((total_sectors - cd.sector) * SECTOR_SIZE, TRUE));
261 }
262 }
263
264 /*-----------------------------------------------------------------------------
265 This function writes a single byte to the CD-ROM image. On the first pass (in
266 which cd.handle < 0), it does not actually write anything but merely updates
267 the file pointer as though the byte had been written.
268 -----------------------------------------------------------------------------*/
269
270 static void write_byte(BYTE x)
271 {
272 if (cd.file != NULL)
273 {
274 cd.buffer[cd.count] = x;
275 if (++cd.count == BUFFER_SIZE)
276 flush_buffer();
277 }
278 if (++cd.offset == SECTOR_SIZE)
279 {
280 cd.sector++;
281 cd.offset = 0;
282 }
283 }
284
285 /*-----------------------------------------------------------------------------
286 These functions write a word or double word to the CD-ROM image with the
287 specified endianity.
288 -----------------------------------------------------------------------------*/
289
290 static void write_little_endian_word(WORD x)
291 {
292 write_byte((BYTE)x);
293 write_byte((BYTE)(x >> 8));
294 }
295
296 static void write_big_endian_word(WORD x)
297 {
298 write_byte((BYTE)(x >> 8));
299 write_byte((BYTE)x);
300 }
301
302 static void write_both_endian_word(WORD x)
303 {
304 write_little_endian_word(x);
305 write_big_endian_word(x);
306 }
307
308 static void write_little_endian_dword(DWORD x)
309 {
310 write_byte((BYTE)x);
311 write_byte((BYTE)(x >> 8));
312 write_byte((BYTE)(x >> 16));
313 write_byte((BYTE)(x >> 24));
314 }
315
316 static void write_big_endian_dword(DWORD x)
317 {
318 write_byte((BYTE)(x >> 24));
319 write_byte((BYTE)(x >> 16));
320 write_byte((BYTE)(x >> 8));
321 write_byte((BYTE)x);
322 }
323
324 static void write_both_endian_dword(DWORD x)
325 {
326 write_little_endian_dword(x);
327 write_big_endian_dword(x);
328 }
329
330 /*-----------------------------------------------------------------------------
331 This function writes enough zeros to fill out the end of a sector, and leaves
332 the file pointer at the beginning of the next sector. If the file pointer is
333 already at the beginning of a sector, it writes nothing.
334 -----------------------------------------------------------------------------*/
335
336 static void fill_sector(void)
337 {
338 while (cd.offset != 0)
339 write_byte(0);
340 }
341
342 /*-----------------------------------------------------------------------------
343 This function writes a string to the CD-ROM image. The terminating \0 is not
344 written.
345 -----------------------------------------------------------------------------*/
346
347 static void write_string(char *s)
348 {
349 while (*s != 0)
350 write_byte(*s++);
351 }
352
353 /*-----------------------------------------------------------------------------
354 This function writes a ansi string as a big endian unicode string to the CD-ROM
355 image. The terminating \0 is not written.
356 -----------------------------------------------------------------------------*/
357
358 static void write_string_as_big_endian_unicode(char *s)
359 {
360 while (*s != 0)
361 {
362 write_byte(0);
363 write_byte(*s++);
364 }
365 }
366
367 /*-----------------------------------------------------------------------------
368 This function writes a block of identical bytes to the CD-ROM image.
369 -----------------------------------------------------------------------------*/
370
371 static void write_block(unsigned count, BYTE value)
372 {
373 while (count != 0)
374 {
375 write_byte(value);
376 count--;
377 }
378 }
379
380 /*-----------------------------------------------------------------------------
381 This function writes a block of identical bige endian words to the CD-ROM image.
382 -----------------------------------------------------------------------------*/
383
384 static void write_word_block(unsigned count, WORD value)
385 {
386 while (count != 0)
387 {
388 write_big_endian_word(value);
389 count--;
390 }
391 }
392
393 /*-----------------------------------------------------------------------------
394 This function writes a directory record to the CD_ROM image.
395 -----------------------------------------------------------------------------*/
396
397 static void
398 write_directory_record(PDIR_RECORD d,
399 DIR_RECORD_TYPE DirType,
400 BOOL joliet)
401 {
402 unsigned identifier_size;
403 unsigned record_size;
404
405 if (joliet)
406 {
407 if (DirType == DOT_RECORD || DirType == DOT_DOT_RECORD)
408 identifier_size = 1;
409 else
410 identifier_size = strlen(d->joliet_name) * 2;
411 }
412 else
413 {
414 switch (DirType)
415 {
416 case DOT_RECORD:
417 case DOT_DOT_RECORD:
418 identifier_size = 1;
419 break;
420 case SUBDIRECTORY_RECORD:
421 /*printf ( "Subdir: %s\n", d->name_on_cd );*/
422 identifier_size = strlen(d->name_on_cd);
423 break;
424 case FILE_RECORD:
425 /*printf ( "File: %s.%s -> %s.%s\n", d->name, d->extension, d->name_on_cd, d->extension_on_cd );*/
426 identifier_size = strlen(d->name_on_cd) + 2;
427 if (d->extension_on_cd[0] != 0)
428 identifier_size += 1 + strlen(d->extension_on_cd);
429 break;
430 default:
431 identifier_size = 1;
432 break;
433 }
434 }
435 record_size = 33 + identifier_size;
436 if ((identifier_size & 1) == 0)
437 record_size++;
438 if (cd.offset + record_size > SECTOR_SIZE)
439 fill_sector();
440 write_byte((BYTE)record_size);
441 write_byte(0); // number of sectors in extended attribute record
442 if (joliet)
443 {
444 write_both_endian_dword(d->joliet_sector);
445 write_both_endian_dword(d->joliet_size);
446 }
447 else
448 {
449 write_both_endian_dword(d->sector);
450 write_both_endian_dword(d->size);
451 }
452 write_byte((BYTE)(d->date_and_time.year - 1900));
453 write_byte(d->date_and_time.month);
454 write_byte(d->date_and_time.day);
455 write_byte(d->date_and_time.hour);
456 write_byte(d->date_and_time.minute);
457 write_byte(d->date_and_time.second);
458 write_byte(0); // GMT offset
459 write_byte(d->flags);
460 write_byte(0); // file unit size for an interleaved file
461 write_byte(0); // interleave gap size for an interleaved file
462 write_both_endian_word((WORD) 1); // volume sequence number
463 write_byte((BYTE)identifier_size);
464 switch (DirType)
465 {
466 case DOT_RECORD:
467 write_byte(0);
468 break;
469 case DOT_DOT_RECORD:
470 write_byte(1);
471 break;
472 case SUBDIRECTORY_RECORD:
473 if (joliet)
474 write_string_as_big_endian_unicode(d->joliet_name);
475 else
476 write_string(d->name_on_cd);
477 break;
478 case FILE_RECORD:
479 if (joliet)
480 {
481 write_string_as_big_endian_unicode(d->joliet_name);
482 }
483 else
484 {
485 write_string(d->name_on_cd);
486 if (d->extension_on_cd[0] != 0)
487 {
488 write_byte('.');
489 write_string(d->extension_on_cd);
490 }
491 write_string(";1");
492 }
493 break;
494 }
495 if ((identifier_size & 1) == 0)
496 write_byte(0);
497 }
498
499 /*-----------------------------------------------------------------------------
500 This function converts the date and time words from an ffblk structure and
501 puts them into a date_and_time structure.
502 -----------------------------------------------------------------------------*/
503
504 static void convert_date_and_time(PDATE_AND_TIME dt, time_t *time)
505 {
506 struct tm *timedef;
507
508 timedef = localtime(time);
509
510 dt->second = timedef->tm_sec;
511 dt->minute = timedef->tm_min;
512 dt->hour = timedef->tm_hour;
513 dt->day = timedef->tm_mday;
514 dt->month = timedef->tm_mon + 1;
515 dt->year = timedef->tm_year + 1900;
516 }
517
518 /*-----------------------------------------------------------------------------
519 This function checks the specified character, if necessary, and
520 generates an error if it is a punctuation mark other than an underscore.
521 It also converts small letters to capital letters and returns the
522 result.
523 -----------------------------------------------------------------------------*/
524
525 static int check_for_punctuation(int c, const char *name)
526 {
527 c = toupper(c & 0xFF);
528 if (!accept_punctuation_marks && !isalnum(c) && c != '_')
529 error_exit("Punctuation mark in %s", name);
530 return c;
531 }
532
533 #if WIN32
534 #define strcasecmp stricmp
535 #endif//WIN32
536
537 /*-----------------------------------------------------------------------------
538 This function checks to see if there's a cdname conflict.
539 -----------------------------------------------------------------------------*/
540
541 int cdname_exists ( PDIR_RECORD d )
542 {
543 PDIR_RECORD p = d->parent->first_record;
544 while ( p )
545 {
546 if ( p != d
547 && !strcasecmp ( p->name_on_cd, d->name_on_cd )
548 && !strcasecmp ( p->extension_on_cd, d->extension_on_cd ) )
549 return 1;
550 p = p->next_in_directory;
551 }
552 return 0;
553 }
554
555 void parse_filename_into_dirrecord ( const char* filename, PDIR_RECORD d, BOOL dir )
556 {
557 const char *s = filename;
558 char *t = d->name_on_cd;
559 char *n = d->name;
560 int joliet_length;
561 int filename_counter;
562 filename_counter = 1;
563 while (*s != 0)
564 {
565 if (*s == '.')
566 {
567 s++;
568 break;
569 }
570
571 if ( (t-d->name_on_cd) < sizeof(d->name_on_cd)-1 )
572 *t++ = check_for_punctuation(*s, filename);
573 else if (!joliet)
574 error_exit ("'%s' is not ISO-9660, aborting...", filename );
575 if ( (n-d->name) < sizeof(d->name)-1 )
576 *n++ = *s;
577 else if (!joliet)
578 error_exit ( "'%s' is not ISO-9660, aborting...", filename );
579 s++;
580 }
581 if (strlen(s) > MAX_EXTENSION_LENGTH)
582 {
583 error_exit ( "'%s' has too long extension for cdmake, aborting...", filename );
584 }
585 *t = 0;
586 strcpy(d->extension, s);
587 t = d->extension_on_cd;
588 while ( *s != 0 )
589 {
590 if ( (t-d->extension_on_cd) < (sizeof(d->extension_on_cd)-1) )
591 *t++ = check_for_punctuation(*s, filename);
592 else if (!joliet)
593 error_exit ( "'%s' is not ISO-9660, aborting...", filename );
594 s++;
595 }
596 *t = 0;
597 *n = 0;
598
599 if ( dir )
600 {
601 if (d->extension[0] != 0)
602 {
603 if (joliet)
604 d->extension_on_cd[0] = 0;
605 else
606 error_exit("Directory with extension %s", filename);
607 }
608 d->flags = DIRECTORY_FLAG;
609 } else
610 d->flags = 0;
611
612
613 filename_counter = 1;
614 while ( cdname_exists ( d ) )
615 {
616
617 // the file name must be least 8 char long
618 if (strlen(d->name_on_cd)<8)
619 error_exit ( "'%s' is a duplicate file name, aborting...", filename );
620
621 if ((d->name_on_cd[8] == '.') && (strlen(d->name_on_cd) < 13))
622 error_exit ( "'%s' is a duplicate file name, aborting...", filename );
623
624 // max 255 times for equal short filename
625 if (filename_counter>255) error_exit ( "'%s' is a duplicate file name, aborting...", filename );
626 d->name_on_cd[8] = '~';
627 memset(&d->name_on_cd[9],0,5);
628 sprintf(&d->name_on_cd[9],"%d",filename_counter);
629 filename_counter++;
630
631 }
632
633 if ( joliet )
634 {
635 joliet_length = strlen(filename);
636 if (joliet_length > 64)
637 error_exit ( "'%s' is not Joliet, aborting...", filename );
638 d->joliet_name = malloc(joliet_length + 1);
639 strcpy(d->joliet_name, filename);
640 }
641 }
642
643 /*-----------------------------------------------------------------------------
644 This function creates a new directory record with the information from the
645 specified ffblk. It links it into the beginning of the directory list
646 for the specified parent and returns a pointer to the new record.
647 -----------------------------------------------------------------------------*/
648
649 #if WIN32
650
651 /* Win32 version */
652 PDIR_RECORD
653 new_directory_record (struct _finddata_t *f,
654 PDIR_RECORD parent)
655 {
656 PDIR_RECORD d;
657
658 d = malloc(sizeof(DIR_RECORD));
659 if (d == NULL)
660 error_exit("Insufficient memory");
661 memset ( d, 0, sizeof(DIR_RECORD) );
662 d->next_in_memory = root.next_in_memory;
663 root.next_in_memory = d;
664
665 /* I need the parent set before calling parse_filename_into_dirrecord(),
666 because that functions checks for duplicate file names*/
667 d->parent = parent;
668 parse_filename_into_dirrecord ( f->name, d, f->attrib & _A_SUBDIR );
669
670 convert_date_and_time(&d->date_and_time, &f->time_write);
671 d->flags |= f->attrib & _A_HIDDEN ? HIDDEN_FLAG : 0;
672 d->size = d->joliet_size = f->size;
673 d->next_in_directory = parent->first_record;
674 parent->first_record = d;
675 return d;
676 }
677
678 #else
679
680 /* Linux version */
681 PDIR_RECORD
682 new_directory_record (struct dirent *entry,
683 struct stat *stbuf,
684 PDIR_RECORD parent)
685 {
686 PDIR_RECORD d;
687 /*
688 char *s;
689 char *t;
690 char *n;
691 */
692
693 d = malloc(sizeof(DIR_RECORD));
694 if (d == NULL)
695 error_exit("Insufficient memory");
696 memset ( d, 0, sizeof(DIR_RECORD) );
697 d->next_in_memory = root.next_in_memory;
698 root.next_in_memory = d;
699
700 /* I need the parent set before calling parse_filename_into_dirrecord(),
701 because that functions checks for duplicate file names*/
702 d->parent = parent;
703 #ifdef HAVE_D_TYPE
704 parse_filename_into_dirrecord ( entry->d_name, d, entry->d_type == DT_DIR );
705 #else
706 parse_filename_into_dirrecord ( entry->d_name, d, S_ISDIR(stbuf->st_mode) );
707 #endif
708
709 convert_date_and_time(&d->date_and_time, &stbuf->st_mtime);
710 d->flags |= entry->d_name[0] == '.' ? HIDDEN_FLAG : 0;
711 d->size = d->joliet_size = stbuf->st_size;
712 d->next_in_directory = parent->first_record;
713 parent->first_record = d;
714 return d;
715 }
716
717 #endif
718
719 /*-----------------------------------------------------------------------------
720 This function compares two directory records according to the ISO9660 rules
721 for directory sorting and returns a negative value if p is before q, or a
722 positive value if p is after q.
723 -----------------------------------------------------------------------------*/
724
725 static int compare_directory_order(PDIR_RECORD p, PDIR_RECORD q)
726 {
727 int n = strcmp(p->name_on_cd, q->name_on_cd);
728 if (n == 0)
729 n = strcmp(p->extension_on_cd, q->extension_on_cd);
730 return n;
731 }
732
733 /*-----------------------------------------------------------------------------
734 This function compares two directory records (which must represent
735 directories) according to the ISO9660 rules for path table sorting and returns
736 a negative value if p is before q, or a positive vlaue if p is after q.
737 -----------------------------------------------------------------------------*/
738
739 static int compare_path_table_order(PDIR_RECORD p, PDIR_RECORD q)
740 {
741 int n = p->level - q->level;
742 if (p == q)
743 return 0;
744 if (n == 0)
745 {
746 n = compare_path_table_order(p->parent, q->parent);
747 if (n == 0)
748 n = compare_directory_order(p, q);
749 }
750 return n;
751 }
752
753 /*-----------------------------------------------------------------------------
754 This function appends the specified string to the buffer source[].
755 -----------------------------------------------------------------------------*/
756
757 static void append_string_to_source(char *s)
758 {
759 while (*s != 0)
760 *end_source++ = *s++;
761 }
762
763 /*-----------------------------------------------------------------------------
764 This function scans all files from the current source[] (which must end in \,
765 and represents a directory already in the database as d),
766 and puts the appropriate directory records into the database in memory, with
767 the specified root. It calls itself recursively to scan all subdirectories.
768 -----------------------------------------------------------------------------*/
769
770 #ifdef WIN32
771
772 static void
773 make_directory_records (PDIR_RECORD d)
774 {
775 PDIR_RECORD new_d;
776 struct _finddata_t f;
777 char *old_end_source;
778 int findhandle;
779
780 d->first_record = NULL;
781 strcpy(end_source, "*.*");
782
783 findhandle =_findfirst(source, &f);
784 if (findhandle != 0)
785 {
786 do
787 {
788 if ((f.attrib & (_A_HIDDEN | _A_SUBDIR)) == 0 && f.name[0] != '.')
789 {
790 if (strcmp(f.name, DIRECTORY_TIMESTAMP) == 0)
791 {
792 convert_date_and_time(&d->date_and_time, &f.time_write);
793 }
794 else
795 {
796 if (verbosity == VERBOSE)
797 {
798 old_end_source = end_source;
799 strcpy(end_source, f.name);
800 printf("%d: file %s\n", d->level, source);
801 end_source = old_end_source;
802 }
803 (void) new_directory_record(&f, d);
804 }
805 }
806 }
807 while (_findnext(findhandle, &f) == 0);
808
809 _findclose(findhandle);
810 }
811
812 strcpy(end_source, "*.*");
813 findhandle= _findfirst(source, &f);
814 if (findhandle)
815 {
816 do
817 {
818 if (f.attrib & _A_SUBDIR && f.name[0] != '.')
819 {
820 old_end_source = end_source;
821 append_string_to_source(f.name);
822 *end_source++ = DIR_SEPARATOR_CHAR;
823 if (verbosity == VERBOSE)
824 {
825 *end_source = 0;
826 printf("%d: directory %s\n", d->level + 1, source);
827 }
828 if (d->level < MAX_LEVEL)
829 {
830 new_d = new_directory_record(&f, d);
831 new_d->next_in_path_table = root.next_in_path_table;
832 root.next_in_path_table = new_d;
833 new_d->level = d->level + 1;
834 make_directory_records(new_d);
835 }
836 else
837 {
838 error_exit("Directory is nested too deep");
839 }
840 end_source = old_end_source;
841 }
842 }
843 while (_findnext(findhandle, &f) == 0);
844
845 _findclose(findhandle);
846 }
847
848 // sort directory
849 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
850 }
851
852 #else
853
854 /* Linux version */
855 static void
856 make_directory_records (PDIR_RECORD d)
857 {
858 PDIR_RECORD new_d;
859 DIR *dirp;
860 struct dirent *entry;
861 char *old_end_source;
862 struct stat stbuf;
863 char buf[MAX_PATH];
864
865 d->first_record = NULL;
866
867 #ifdef HAVE_D_TYPE
868 dirp = opendir(source);
869 if (dirp != NULL)
870 {
871 while ((entry = readdir (dirp)) != NULL)
872 {
873 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
874 continue; // skip self and parent
875
876 if (entry->d_type == DT_REG) // normal file
877 {
878 // Check for an absolute path
879 if (source[0] == DIR_SEPARATOR_CHAR)
880 {
881 strcpy(buf, source);
882 strcat(buf, DIR_SEPARATOR_STRING);
883 strcat(buf, entry->d_name);
884 }
885 else
886 {
887 getcwd(buf, sizeof(buf));
888 strcat(buf, DIR_SEPARATOR_STRING);
889 strcat(buf, source);
890 strcat(buf, entry->d_name);
891 }
892
893 if (stat(buf, &stbuf) == -1)
894 {
895 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
896 return;
897 }
898
899 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
900 {
901 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
902 }
903 else
904 {
905 if (verbosity == VERBOSE)
906 {
907 printf("%d: file %s\n", d->level, buf);
908 }
909 (void) new_directory_record(entry, &stbuf, d);
910 }
911 }
912 }
913 closedir(dirp);
914 }
915 else
916 {
917 error_exit("Can't open %s\n", source);
918 return;
919 }
920
921 dirp = opendir(source);
922 if (dirp != NULL)
923 {
924 while ((entry = readdir (dirp)) != NULL)
925 {
926 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
927 continue; // skip self and parent
928
929 if (entry->d_type == DT_DIR) // directory
930 {
931 old_end_source = end_source;
932 append_string_to_source(entry->d_name);
933 *end_source++ = DIR_SEPARATOR_CHAR;
934 *end_source = 0;
935 if (verbosity == VERBOSE)
936 {
937 printf("%d: directory %s\n", d->level + 1, source);
938 }
939 if (d->level < MAX_LEVEL)
940 {
941 // Check for an absolute path
942 if (source[0] == DIR_SEPARATOR_CHAR)
943 {
944 strcpy(buf, source);
945 }
946 else
947 {
948 getcwd(buf, sizeof(buf));
949 strcat(buf, DIR_SEPARATOR_STRING);
950 strcat(buf, source);
951 }
952
953 if (stat(buf, &stbuf) == -1)
954 {
955 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
956 return;
957 }
958 new_d = new_directory_record(entry, &stbuf, d);
959 new_d->next_in_path_table = root.next_in_path_table;
960 root.next_in_path_table = new_d;
961 new_d->level = d->level + 1;
962 make_directory_records(new_d);
963 }
964 else
965 {
966 error_exit("Directory is nested too deep");
967 }
968 end_source = old_end_source;
969 *end_source = 0;
970 }
971 }
972 closedir(dirp);
973 }
974 else
975 {
976 error_exit("Can't open %s\n", source);
977 return;
978 }
979
980 #else
981
982 dirp = opendir(source);
983 if (dirp != NULL)
984 {
985 while ((entry = readdir (dirp)) != NULL)
986 {
987 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
988 continue; // skip self and parent
989
990 // Check for an absolute path
991 if (source[0] == DIR_SEPARATOR_CHAR)
992 {
993 strcpy(buf, source);
994 strcat(buf, DIR_SEPARATOR_STRING);
995 strcat(buf, entry->d_name);
996 }
997 else
998 {
999 getcwd(buf, sizeof(buf));
1000 strcat(buf, DIR_SEPARATOR_STRING);
1001 strcat(buf, source);
1002 strcat(buf, entry->d_name);
1003 }
1004
1005 if (stat(buf, &stbuf) == -1)
1006 {
1007 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
1008 return;
1009 }
1010
1011 if (S_ISDIR(stbuf.st_mode))
1012 {
1013 old_end_source = end_source;
1014 append_string_to_source(entry->d_name);
1015 *end_source++ = DIR_SEPARATOR_CHAR;
1016 *end_source = 0;
1017 if (verbosity == VERBOSE)
1018 {
1019 printf("%d: directory %s\n", d->level + 1, source);
1020 }
1021
1022 if (d->level < MAX_LEVEL)
1023 {
1024 new_d = new_directory_record(entry, &stbuf, d);
1025 new_d->next_in_path_table = root.next_in_path_table;
1026 root.next_in_path_table = new_d;
1027 new_d->level = d->level + 1;
1028 make_directory_records(new_d);
1029 }
1030 else
1031 {
1032 error_exit("Directory is nested too deep");
1033 }
1034
1035 end_source = old_end_source;
1036 *end_source = 0;
1037 }
1038 else if (S_ISREG(stbuf.st_mode))
1039 {
1040 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
1041 {
1042 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
1043 }
1044 else
1045 {
1046 if (verbosity == VERBOSE)
1047 {
1048 printf("%d: file %s\n", d->level, buf);
1049 }
1050 (void) new_directory_record(entry, &stbuf, d);
1051 }
1052 }
1053 }
1054 closedir (dirp);
1055 }
1056 else
1057 {
1058 error_exit("Can't open %s\n", source);
1059 return;
1060 }
1061
1062 #endif
1063
1064 // sort directory
1065 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
1066 }
1067
1068 #endif
1069
1070 /*-----------------------------------------------------------------------------
1071 This function loads the file specifications for the file or directory
1072 corresponding to the specified directory record into the source[] buffer. It
1073 is recursive.
1074 -----------------------------------------------------------------------------*/
1075
1076 static void get_file_specifications(PDIR_RECORD d)
1077 {
1078 if (d != &root)
1079 {
1080 get_file_specifications(d->parent);
1081 append_string_to_source(d->name);
1082 if (((d->flags & DIRECTORY_FLAG) == 0 || joliet) && d->extension[0] != 0)
1083 {
1084 *end_source++ = '.';
1085 append_string_to_source(d->extension);
1086 }
1087 if (d->flags & DIRECTORY_FLAG)
1088 *end_source++ = DIR_SEPARATOR_CHAR;
1089 }
1090 }
1091
1092 static void pass(void)
1093 {
1094 PDIR_RECORD d;
1095 PDIR_RECORD q;
1096 unsigned int i;
1097 char *t;
1098 unsigned int index;
1099 unsigned int name_length;
1100 DWORD size;
1101 DWORD number_of_sectors;
1102 char *old_end_source;
1103 int n;
1104 FILE *file;
1105
1106 // first 16 sectors are zeros
1107
1108 write_block(16 * SECTOR_SIZE, 0);
1109
1110 // Primary Volume Descriptor
1111
1112 write_string("\1CD001\1");
1113 write_byte(0);
1114 write_block(32, ' '); // system identifier
1115
1116 t = volume_label;
1117 for (i = 0; i < 32; i++)
1118 write_byte( (BYTE)( (*t != 0) ? toupper(*t++) : ' ' ) );
1119
1120 write_block(8, 0);
1121 write_both_endian_dword(total_sectors);
1122 write_block(32, 0);
1123 write_both_endian_word((WORD) 1); // volume set size
1124 write_both_endian_word((WORD) 1); // volume sequence number
1125 write_both_endian_word((WORD) 2048); // sector size
1126 write_both_endian_dword(path_table_size);
1127 write_little_endian_dword(little_endian_path_table_sector);
1128 write_little_endian_dword((DWORD) 0); // second little endian path table
1129 write_big_endian_dword(big_endian_path_table_sector);
1130 write_big_endian_dword((DWORD) 0); // second big endian path table
1131 write_directory_record(&root, DOT_RECORD, FALSE);
1132 write_block(128, ' '); // volume set identifier
1133 write_block(128, ' '); // publisher identifier
1134 write_block(128, ' '); // data preparer identifier
1135 write_block(128, ' '); // application identifier
1136 write_block(37, ' '); // copyright file identifier
1137 write_block(37, ' '); // abstract file identifier
1138 write_block(37, ' '); // bibliographic file identifier
1139 write_string("0000000000000000"); // volume creation
1140 write_byte(0);
1141 write_string("0000000000000000"); // most recent modification
1142 write_byte(0);
1143 write_string("0000000000000000"); // volume expires
1144 write_byte(0);
1145 write_string("0000000000000000"); // volume is effective
1146 write_byte(0);
1147 write_byte(1);
1148 write_byte(0);
1149 fill_sector();
1150
1151
1152 // Boot Volume Descriptor
1153
1154 if (eltorito == TRUE)
1155 {
1156 write_byte(0);
1157 write_string("CD001\1");
1158 write_string("EL TORITO SPECIFICATION"); // identifier
1159 write_block(9, 0); // padding
1160 write_block(32, 0); // unused
1161 write_little_endian_dword(boot_catalog_sector); // pointer to boot catalog
1162 fill_sector();
1163 }
1164
1165 // Supplementary Volume Descriptor
1166
1167 if (joliet)
1168 {
1169 write_string("\2CD001\1");
1170 write_byte(0);
1171
1172 write_word_block(16, L' '); // system identifier
1173
1174 t = volume_label;
1175 for (i = 0; i < 16; i++)
1176 write_big_endian_word( (BYTE)( (*t != 0) ? *t++ : ' ' ) );
1177
1178 write_block(8, 0);
1179 write_both_endian_dword(total_sectors);
1180 write_string("%/E");
1181 write_block(29, 0);
1182 write_both_endian_word((WORD) 1); // volume set size
1183 write_both_endian_word((WORD) 1); // volume sequence number
1184 write_both_endian_word((WORD) 2048); // sector size
1185 write_both_endian_dword(joliet_path_table_size);
1186 write_little_endian_dword(joliet_little_endian_path_table_sector);
1187 write_little_endian_dword((DWORD) 0); // second little endian path table
1188 write_big_endian_dword(joliet_big_endian_path_table_sector);
1189 write_big_endian_dword((DWORD) 0); // second big endian path table
1190 write_directory_record(&root, DOT_RECORD, TRUE);
1191 write_word_block(64, ' '); // volume set identifier
1192 write_word_block(64, ' '); // publisher identifier
1193 write_word_block(64, ' '); // data preparer identifier
1194 write_word_block(64, ' '); // application identifier
1195 write_block(37, ' '); // copyright file identifier
1196 write_block(37, ' '); // abstract file identifier
1197 write_block(37, ' '); // bibliographic file identifier
1198 write_string("0000000000000000"); // volume creation
1199 write_byte(0);
1200 write_string("0000000000000000"); // most recent modification
1201 write_byte(0);
1202 write_string("0000000000000000"); // volume expires
1203 write_byte(0);
1204 write_string("0000000000000000"); // volume is effective
1205 write_byte(0);
1206 write_byte(1);
1207 write_byte(0);
1208 fill_sector();
1209 }
1210
1211
1212 // Volume Descriptor Set Terminator
1213 write_string("\377CD001\1");
1214 fill_sector();
1215
1216 // Boot Catalog
1217 if (eltorito == TRUE)
1218 {
1219 boot_catalog_sector = cd.sector;
1220
1221 // Validation entry
1222 write_byte(1);
1223 write_byte(0); // x86 boot code
1224 write_little_endian_word(0); // reserved
1225 write_string("ReactOS Foundation");
1226 write_block(6, 0); // padding
1227 write_little_endian_word(0x62E); // checksum
1228 write_little_endian_word(0xAA55); // signature
1229
1230 // default entry
1231 write_byte(0x88); // bootable
1232 write_byte(0); // no emulation
1233 write_big_endian_word(0); // load segment = default (0x07c0)
1234 write_byte(0); // partition type
1235 write_byte(0); // unused
1236 write_little_endian_word(boot_image_size); // sector count
1237 write_little_endian_dword(boot_image_sector); // sector
1238
1239 fill_sector();
1240 }
1241
1242 // Boot Image
1243 if (eltorito == TRUE)
1244 {
1245 boot_image_sector = cd.sector;
1246
1247 file = fopen(bootimage, "rb");
1248 if (file == NULL)
1249 error_exit("Can't open %s\n", bootimage);
1250 fseek(file, 0, SEEK_END);
1251 size = ftell(file);
1252 fseek(file, 0, SEEK_SET);
1253 if (size == 0 || (size % 2048))
1254 error_exit("Invalid boot image size (%lu bytes)\n", size);
1255 boot_image_size = size / 512;
1256 while (size > 0)
1257 {
1258 n = BUFFER_SIZE - cd.count;
1259 if ((DWORD) n > size)
1260 n = size;
1261 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
1262 {
1263 fclose(file);
1264 error_exit("Read error in file %s\n", bootimage);
1265 }
1266 cd.count += n;
1267 if (cd.count == BUFFER_SIZE)
1268 flush_buffer();
1269 cd.sector += n / SECTOR_SIZE;
1270 cd.offset += n % SECTOR_SIZE;
1271 size -= n;
1272 }
1273 fclose(file);
1274 // fill_sector();
1275 }
1276
1277 // Little Endian Path Table
1278 little_endian_path_table_sector = cd.sector;
1279 write_byte(1);
1280 write_byte(0); // number of sectors in extended attribute record
1281 write_little_endian_dword(root.sector);
1282 write_little_endian_word((WORD) 1);
1283 write_byte(0);
1284 write_byte(0);
1285
1286 index = 1;
1287 root.path_table_index = 1;
1288 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1289 {
1290 name_length = strlen(d->name_on_cd);
1291 write_byte((BYTE)name_length);
1292 write_byte(0); // number of sectors in extended attribute record
1293 write_little_endian_dword(d->sector);
1294 write_little_endian_word(d->parent->path_table_index);
1295 write_string(d->name_on_cd);
1296 if (name_length & 1)
1297 write_byte(0);
1298 d->path_table_index = ++index;
1299 }
1300
1301 path_table_size = (cd.sector - little_endian_path_table_sector) *
1302 SECTOR_SIZE + cd.offset;
1303 fill_sector();
1304
1305 // Big Endian Path Table
1306
1307 big_endian_path_table_sector = cd.sector;
1308 write_byte(1);
1309 write_byte(0); // number of sectors in extended attribute record
1310 write_big_endian_dword(root.sector);
1311 write_big_endian_word((WORD) 1);
1312 write_byte(0);
1313 write_byte(0);
1314
1315 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1316 {
1317 name_length = strlen(d->name_on_cd);
1318 write_byte((BYTE)name_length);
1319 write_byte(0); // number of sectors in extended attribute record
1320 write_big_endian_dword(d->sector);
1321 write_big_endian_word(d->parent->path_table_index);
1322 write_string(d->name_on_cd);
1323 if (name_length & 1)
1324 write_byte(0);
1325 }
1326 fill_sector();
1327
1328 if (joliet)
1329 {
1330 // Little Endian Path Table
1331
1332 joliet_little_endian_path_table_sector = cd.sector;
1333 write_byte(1);
1334 write_byte(0); // number of sectors in extended attribute record
1335 write_little_endian_dword(root.joliet_sector);
1336 write_little_endian_word((WORD) 1);
1337 write_byte(0);
1338 write_byte(0);
1339
1340 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1341 {
1342 name_length = strlen(d->joliet_name) * 2;
1343 write_byte((BYTE)name_length);
1344 write_byte(0); // number of sectors in extended attribute record
1345 write_little_endian_dword(d->joliet_sector);
1346 write_little_endian_word(d->parent->path_table_index);
1347 write_string_as_big_endian_unicode(d->joliet_name);
1348 }
1349
1350 joliet_path_table_size = (cd.sector - joliet_little_endian_path_table_sector) *
1351 SECTOR_SIZE + cd.offset;
1352 fill_sector();
1353
1354 // Big Endian Path Table
1355
1356 joliet_big_endian_path_table_sector = cd.sector;
1357 write_byte(1);
1358 write_byte(0); // number of sectors in extended attribute record
1359 write_big_endian_dword(root.joliet_sector);
1360 write_big_endian_word((WORD) 1);
1361 write_byte(0);
1362 write_byte(0);
1363
1364 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1365 {
1366 name_length = strlen(d->joliet_name) * 2;
1367 write_byte((BYTE)name_length);
1368 write_byte(0); // number of sectors in extended attribute record
1369 write_big_endian_dword(d->joliet_sector);
1370 write_big_endian_word(d->parent->path_table_index);
1371 write_string_as_big_endian_unicode(d->joliet_name);
1372 }
1373 fill_sector();
1374 }
1375
1376 // directories and files
1377 for (d = &root; d != NULL; d = d->next_in_path_table)
1378 {
1379 // write directory
1380 d->sector = cd.sector;
1381 write_directory_record(d, DOT_RECORD, FALSE);
1382 write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD, FALSE);
1383 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1384 {
1385 write_directory_record(q,
1386 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1387 FALSE);
1388 }
1389 fill_sector();
1390 d->size = (cd.sector - d->sector) * SECTOR_SIZE;
1391
1392 // write directory for joliet
1393 if (joliet)
1394 {
1395 d->joliet_sector = cd.sector;
1396 write_directory_record(d, DOT_RECORD, TRUE);
1397 write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD, TRUE);
1398 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1399 {
1400 write_directory_record(q,
1401 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1402 TRUE);
1403 }
1404 fill_sector();
1405 d->joliet_size = (cd.sector - d->joliet_sector) * SECTOR_SIZE;
1406 bytes_in_directories += d->joliet_size;
1407 }
1408
1409 number_of_directories++;
1410 bytes_in_directories += d->size;
1411
1412 // write file data
1413 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1414 {
1415 if ((q->flags & DIRECTORY_FLAG) == 0)
1416 {
1417 q->sector = q->joliet_sector = cd.sector;
1418 size = q->size;
1419 if (cd.file == NULL)
1420 {
1421 number_of_sectors = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
1422 cd.sector += number_of_sectors;
1423 number_of_files++;
1424 bytes_in_files += size;
1425 unused_bytes_at_ends_of_files +=
1426 number_of_sectors * SECTOR_SIZE - size;
1427 }
1428 else
1429 {
1430 old_end_source = end_source;
1431 get_file_specifications(q);
1432 *end_source = 0;
1433 if (verbosity == VERBOSE)
1434 printf("Writing %s\n", source);
1435 file = fopen(source, "rb");
1436 if (file == NULL)
1437 error_exit("Can't open %s\n", source);
1438 fseek(file, 0, SEEK_SET);
1439 while (size > 0)
1440 {
1441 n = BUFFER_SIZE - cd.count;
1442 if ((DWORD) n > size)
1443 n = size;
1444 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
1445 {
1446 fclose(file);
1447 error_exit("Read error in file %s\n", source);
1448 }
1449 cd.count += n;
1450 if (cd.count == BUFFER_SIZE)
1451 flush_buffer();
1452 cd.sector += n / SECTOR_SIZE;
1453 cd.offset += n % SECTOR_SIZE;
1454 size -= n;
1455 }
1456 fclose(file);
1457 end_source = old_end_source;
1458 fill_sector();
1459 }
1460 }
1461 }
1462 }
1463
1464 total_sectors = (DWORD)cd.sector;
1465 }
1466
1467 static char HELP[] =
1468 "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] [-j] source volume image\n"
1469 "\n"
1470 " source specifications of base directory containing all files to\n"
1471 " be written to CD-ROM image\n"
1472 " volume volume label\n"
1473 " image image file or device\n"
1474 " -q quiet mode - display nothing but error messages\n"
1475 " -v verbose mode - display file information as files are\n"
1476 " scanned and written - overrides -p option\n"
1477 " -p show progress while writing\n"
1478 " -s N abort operation before beginning write if image will be\n"
1479 " larger than N megabytes (i.e. 1024*1024*N bytes)\n"
1480 " -m accept punctuation marks other than underscores in\n"
1481 " names and extensions\n"
1482 " -b bootimage create bootable ElTorito CD-ROM using 'no emulation' mode\n"
1483 " -j generate Joliet filename records\n";
1484
1485 /*-----------------------------------------------------------------------------
1486 Program execution starts here.
1487 -----------------------------------------------------------------------------*/
1488
1489 int main(int argc, char **argv)
1490 {
1491 BOOL q_option = FALSE;
1492 BOOL v_option = FALSE;
1493 int i;
1494 char *t;
1495
1496 if (argc < 2)
1497 {
1498 puts(HELP);
1499 return 1;
1500 }
1501
1502 if (setjmp(error))
1503 return 1;
1504
1505 // initialize root directory
1506
1507 cd.buffer = malloc (BUFFER_SIZE);
1508 if (cd.buffer == NULL)
1509 error_exit("Insufficient memory");
1510
1511 memset(&root, 0, sizeof(root));
1512 root.level = 1;
1513 root.flags = DIRECTORY_FLAG;
1514
1515 // initialize CD-ROM write buffer
1516
1517 cd.file = NULL;
1518 cd.filespecs[0] = 0;
1519
1520 // initialize parameters
1521
1522 verbosity = NORMAL;
1523 size_limit = 0;
1524 show_progress = FALSE;
1525 accept_punctuation_marks = FALSE;
1526 source[0] = 0;
1527 volume_label[0] = 0;
1528
1529 eltorito = FALSE;
1530
1531 // scan command line arguments
1532
1533 for (i = 1; i < argc; i++)
1534 {
1535 if (memcmp(argv[i], "-s", 2) == 0)
1536 {
1537 t = argv[i] + 2;
1538 if (*t == 0)
1539 {
1540 if (++i < argc)
1541 t = argv[i];
1542 else
1543 error_exit("Missing size limit parameter");
1544 }
1545 while (isdigit(*t))
1546 size_limit = size_limit * 10 + *t++ - '0';
1547 if (size_limit < 1 || size_limit > 800)
1548 error_exit("Invalid size limit");
1549 size_limit <<= 9; // convert megabyte to sector count
1550 }
1551 else if (strcmp(argv[i], "-q") == 0)
1552 q_option = TRUE;
1553 else if (strcmp(argv[i], "-v") == 0)
1554 v_option = TRUE;
1555 else if (strcmp(argv[i], "-p") == 0)
1556 show_progress = TRUE;
1557 else if (strcmp(argv[i], "-m") == 0)
1558 accept_punctuation_marks = TRUE;
1559 else if (strcmp(argv[i], "-j") == 0)
1560 joliet = TRUE;
1561 else if (strcmp(argv[i], "-b") == 0)
1562 {
1563 strcpy(bootimage, argv[++i]);
1564 eltorito = TRUE;
1565 }
1566 else if (i + 2 < argc)
1567 {
1568 strcpy(source, argv[i++]);
1569 strncpy(volume_label, argv[i++], sizeof(volume_label) - 1);
1570 strcpy(cd.filespecs, argv[i]);
1571 }
1572 else
1573 error_exit("Missing command line argument");
1574 }
1575 if (v_option)
1576 {
1577 show_progress = FALSE;
1578 verbosity = VERBOSE;
1579 }
1580 else if (q_option)
1581 {
1582 verbosity = QUIET;
1583 show_progress = FALSE;
1584 }
1585 if (source[0] == 0)
1586 error_exit("Missing source directory");
1587 if (volume_label[0] == 0)
1588 error_exit("Missing volume label");
1589 if (cd.filespecs[0] == 0)
1590 error_exit("Missing image file specifications");
1591
1592
1593 // set source[] and end_source to source directory, with a terminating directory separator
1594
1595 end_source = source + strlen(source);
1596 if (end_source[-1] == ':')
1597 *end_source++ = '.';
1598 if (end_source[-1] != DIR_SEPARATOR_CHAR)
1599 *end_source++ = DIR_SEPARATOR_CHAR;
1600
1601 // scan all files and create directory structure in memory
1602
1603 make_directory_records(&root);
1604
1605 // sort path table entries
1606
1607 root.next_in_path_table = sort_linked_list(root.next_in_path_table, 1,
1608 compare_path_table_order);
1609
1610 // initialize CD-ROM write buffer
1611
1612 cd.file = NULL;
1613 cd.sector = 0;
1614 cd.offset = 0;
1615 cd.count = 0;
1616
1617 // make non-writing pass over directory structure to obtain the proper
1618 // sector numbers and offsets and to determine the size of the image
1619
1620 number_of_files = bytes_in_files = number_of_directories =
1621 bytes_in_directories = unused_bytes_at_ends_of_files =0;
1622 pass();
1623
1624 if (verbosity >= NORMAL)
1625 {
1626 printf("%s bytes ", edit_with_commas(bytes_in_files, TRUE));
1627 printf("in %s files\n", edit_with_commas(number_of_files, FALSE));
1628 printf("%s unused bytes at ends of files\n",
1629 edit_with_commas(unused_bytes_at_ends_of_files, TRUE));
1630 printf("%s bytes ", edit_with_commas(bytes_in_directories, TRUE));
1631 printf("in %s directories\n",
1632 edit_with_commas(number_of_directories, FALSE));
1633 printf("%s other bytes\n", edit_with_commas(root.sector * SECTOR_SIZE, TRUE));
1634 puts("-------------");
1635 printf("%s total bytes\n",
1636 edit_with_commas(total_sectors * SECTOR_SIZE, TRUE));
1637 puts("=============");
1638 }
1639
1640 if (size_limit != 0 && total_sectors > size_limit)
1641 error_exit("Size limit exceeded");
1642
1643 // re-initialize CD-ROM write buffer
1644
1645 cd.file = fopen(cd.filespecs, "w+b");
1646 if (cd.file == NULL)
1647 error_exit("Can't open image file %s", cd.filespecs);
1648 cd.sector = 0;
1649 cd.offset = 0;
1650 cd.count = 0;
1651
1652
1653 // make writing pass over directory structure
1654
1655 pass();
1656
1657 if (cd.count > 0)
1658 flush_buffer();
1659 if (show_progress)
1660 printf("\r \n");
1661 if (fclose(cd.file) != 0)
1662 {
1663 cd.file = NULL;
1664 error_exit("File write error in image file %s", cd.filespecs);
1665 }
1666
1667 if (verbosity >= NORMAL)
1668 puts("CD-ROM image made successfully");
1669
1670 release_memory();
1671 return 0;
1672 }
1673
1674
1675 /* EOF */