5 http://www.alumni.caltech.edu/~pje/
12 by Casper S. Hornstrup
13 chorns@users.sourceforge.net
16 /* According to his website, this file was released into the public domain by Phillip J. Erdelsky */
29 #include <sys/types.h>
39 #define DIR_SEPARATOR_CHAR '/'
40 #define DIR_SEPARATOR_STRING "/"
42 #define DIR_SEPARATOR_CHAR '\\'
43 #define DIR_SEPARATOR_STRING "\\"
47 typedef unsigned char BYTE;
48 typedef unsigned short WORD;
49 typedef unsigned long DWORD;
55 // file system parameters
58 #define MAX_NAME_LENGTH 8
59 #define MAX_EXTENSION_LENGTH 3
60 #define SECTOR_SIZE 2048
61 #define BUFFER_SIZE (8 * SECTOR_SIZE)
63 const BYTE HIDDEN_FLAG = 1;
64 const BYTE DIRECTORY_FLAG = 2;
70 DWORD sector; // sector to receive next byte
71 int offset; // offset of next byte in sector
72 int count; // number of bytes in buffer
77 typedef struct date_and_time
85 } DATE_AND_TIME, *PDATE_AND_TIME;
87 typedef struct directory_record
89 struct directory_record *next_in_directory;
90 struct directory_record *next_in_path_table; /* directory record only */
91 struct directory_record *next_in_memory;
92 struct directory_record *first_record; /* directory record only */
93 struct directory_record *parent;
95 char name[MAX_NAME_LENGTH+1];
96 char name_on_cd[MAX_NAME_LENGTH+1];
97 char extension[MAX_EXTENSION_LENGTH+1];
98 char extension_on_cd[MAX_EXTENSION_LENGTH+1];
99 DATE_AND_TIME date_and_time;
102 unsigned level; /* directory record only */
103 WORD path_table_index; /* directory record only */
104 } DIR_RECORD, *PDIR_RECORD;
107 typedef enum directory_record_type
113 } DIR_RECORD_TYPE, *PDIR_RECORD_TYPE;
116 PDIR_RECORD sort_linked_list(PDIR_RECORD,
117 unsigned, int (*)(PDIR_RECORD, PDIR_RECORD));
120 static char DIRECTORY_TIMESTAMP[] = "~Y$'KOR$.3K&";
122 static jmp_buf error;
123 static struct cd_image cd;
125 char volume_label[32];
129 enum {QUIET, NORMAL, VERBOSE} verbosity;
132 BOOL accept_punctuation_marks;
135 DWORD path_table_size;
136 DWORD little_endian_path_table_sector;
137 DWORD big_endian_path_table_sector;
138 DWORD number_of_files;
139 DWORD bytes_in_files;
140 DWORD unused_bytes_at_ends_of_files;
141 DWORD number_of_directories;
142 DWORD bytes_in_directories;
146 DWORD boot_catalog_sector;
147 DWORD boot_image_sector;
149 /*-----------------------------------------------------------------------------
150 This function edits a 32-bit unsigned number into a comma-delimited form, such
151 as 4,294,967,295 for the largest possible number, and returns a pointer to a
152 static buffer containing the result. It suppresses leading zeros and commas,
153 but optionally pads the result with blanks at the left so the result is always
154 exactly 13 characters long (excluding the terminating zero).
156 CAUTION: A statement containing more than one call on this function, such as
157 printf("%s, %s", edit_with_commas(a), edit_with_commas(b)), will produce
158 incorrect results because all calls use the same static bufffer.
159 -----------------------------------------------------------------------------*/
161 static char *edit_with_commas(DWORD x, BOOL pad)
167 if (i % 4 == 2) s[--i] = ',';
168 s[--i] = x % 10 + '0';
169 } while ((x/=10) != 0);
172 while (i > 0) s[--i] = ' ';
177 /*-----------------------------------------------------------------------------
178 This function releases all allocated memory blocks.
179 -----------------------------------------------------------------------------*/
181 static void release_memory(void)
183 while (root.next_in_memory != NULL)
185 struct directory_record *next =
186 root.next_in_memory->next_in_memory;
187 free (root.next_in_memory);
188 root.next_in_memory = next;
190 if (cd.buffer != NULL)
197 /*-----------------------------------------------------------------------------
198 This function edits and displays an error message and then jumps back to the
199 error exit point in main().
200 -----------------------------------------------------------------------------*/
202 #define error_exit(fmt,args...) \
204 printf(fmt,##args); \
206 if (cd.file != NULL) \
212 /*-----------------------------------------------------------------------------
213 This function, which is called only on the second pass, and only when the
214 buffer is not empty, flushes the buffer to the CD-ROM image.
215 -----------------------------------------------------------------------------*/
217 static void flush_buffer(void)
219 if (fwrite(cd.buffer, cd.count, 1, cd.file) < 1)
220 error_exit("File write error");
225 edit_with_commas((total_sectors - cd.sector) * SECTOR_SIZE, TRUE));
229 /*-----------------------------------------------------------------------------
230 This function writes a single byte to the CD-ROM image. On the first pass (in
231 which cd.handle < 0), it does not actually write anything but merely updates
232 the file pointer as though the byte had been written.
233 -----------------------------------------------------------------------------*/
235 static void write_byte(BYTE x)
239 cd.buffer[cd.count] = x;
240 if (++cd.count == BUFFER_SIZE)
243 if (++cd.offset == SECTOR_SIZE)
250 /*-----------------------------------------------------------------------------
251 These functions write a word or double word to the CD-ROM image with the
253 -----------------------------------------------------------------------------*/
255 static void write_little_endian_word(WORD x)
261 static void write_big_endian_word(WORD x)
267 static void write_both_endian_word(WORD x)
269 write_little_endian_word(x);
270 write_big_endian_word(x);
273 static void write_little_endian_dword(DWORD x)
281 static void write_big_endian_dword(DWORD x)
289 static void write_both_endian_dword(DWORD x)
291 write_little_endian_dword(x);
292 write_big_endian_dword(x);
295 /*-----------------------------------------------------------------------------
296 This function writes enough zeros to fill out the end of a sector, and leaves
297 the file pointer at the beginning of the next sector. If the file pointer is
298 already at the beginning of a sector, it writes nothing.
299 -----------------------------------------------------------------------------*/
301 static void fill_sector(void)
303 while (cd.offset != 0)
307 /*-----------------------------------------------------------------------------
308 This function writes a string to the CD-ROM image. The terminating \0 is not
310 -----------------------------------------------------------------------------*/
312 static void write_string(char *s)
318 /*-----------------------------------------------------------------------------
319 This function writes a block of identical bytes to the CD-ROM image.
320 -----------------------------------------------------------------------------*/
322 static void write_block(unsigned count, BYTE value)
331 /*-----------------------------------------------------------------------------
332 This function writes a directory record to the CD_ROM image.
333 -----------------------------------------------------------------------------*/
336 write_directory_record(PDIR_RECORD d,
337 DIR_RECORD_TYPE DirType)
339 unsigned identifier_size;
340 unsigned record_size;
348 case SUBDIRECTORY_RECORD:
349 identifier_size = strlen(d->name_on_cd);
352 identifier_size = strlen(d->name_on_cd) + 2;
353 if (d->extension_on_cd[0] != 0)
354 identifier_size += 1 + strlen(d->extension_on_cd);
357 record_size = 33 + identifier_size;
358 if ((identifier_size & 1) == 0)
360 if (cd.offset + record_size > SECTOR_SIZE)
362 write_byte(record_size);
363 write_byte(0); // number of sectors in extended attribute record
364 write_both_endian_dword(d->sector);
365 write_both_endian_dword(d->size);
366 write_byte(d->date_and_time.year - 1900);
367 write_byte(d->date_and_time.month);
368 write_byte(d->date_and_time.day);
369 write_byte(d->date_and_time.hour);
370 write_byte(d->date_and_time.minute);
371 write_byte(d->date_and_time.second);
372 write_byte(0); // GMT offset
373 write_byte(d->flags);
374 write_byte(0); // file unit size for an interleaved file
375 write_byte(0); // interleave gap size for an interleaved file
376 write_both_endian_word((WORD) 1); // volume sequence number
377 write_byte(identifier_size);
386 case SUBDIRECTORY_RECORD:
387 write_string(d->name_on_cd);
390 write_string(d->name_on_cd);
391 if (d->extension_on_cd[0] != 0)
394 write_string(d->extension_on_cd);
399 if ((identifier_size & 1) == 0)
403 /*-----------------------------------------------------------------------------
404 This function converts the date and time words from an ffblk structure and
405 puts them into a date_and_time structure.
406 -----------------------------------------------------------------------------*/
408 static void convert_date_and_time(PDATE_AND_TIME dt, time_t *time)
412 timedef = localtime(time);
414 dt->second = timedef->tm_sec;
415 dt->minute = timedef->tm_min;
416 dt->hour = timedef->tm_hour;
417 dt->day = timedef->tm_mday;
418 dt->month = timedef->tm_mon;
419 dt->year = timedef->tm_year + 1900;
422 /*-----------------------------------------------------------------------------
423 This function checks the specified character, if necessary, and
424 generates an error if it is a punctuation mark other than an underscore.
425 It also converts small letters to capital letters and returns the
427 -----------------------------------------------------------------------------*/
429 static int check_for_punctuation(int c, char *name)
431 c = toupper(c & 0xFF);
432 if (!accept_punctuation_marks && !isalnum(c) && c != '_')
433 error_exit("Punctuation mark in %s", name);
437 /*-----------------------------------------------------------------------------
438 This function creates a new directory record with the information from the
439 specified ffblk. It links it into the beginning of the directory list
440 for the specified parent and returns a pointer to the new record.
441 -----------------------------------------------------------------------------*/
447 new_directory_record (struct _finddata_t *f,
455 d = malloc(sizeof(DIR_RECORD));
457 error_exit("Insufficient memory");
458 d->next_in_memory = root.next_in_memory;
459 root.next_in_memory = d;
473 *t++ = check_for_punctuation(*s, f->name);
479 strcpy(d->extension, s);
480 t = d->extension_on_cd;
482 *t++ = check_for_punctuation(*s++, f->name);
486 convert_date_and_time(&d->date_and_time, &f->time_create);
487 if (f->attrib & _A_SUBDIR)
489 if (d->extension[0] != 0)
490 error_exit("Directory with extension %s", f->name);
491 d->flags = DIRECTORY_FLAG;
494 d->flags = f->attrib & _A_HIDDEN ? HIDDEN_FLAG : 0;
496 d->next_in_directory = parent->first_record;
497 parent->first_record = d;
506 new_directory_record (struct dirent *entry,
515 d = malloc(sizeof(DIR_RECORD));
517 error_exit("Insufficient memory");
518 d->next_in_memory = root.next_in_memory;
519 root.next_in_memory = d;
533 *t++ = check_for_punctuation(*s, entry->d_name);
539 strcpy(d->extension, s);
540 t = d->extension_on_cd;
542 *t++ = check_for_punctuation(*s++, entry->d_name);
547 convert_date_and_time(&d->date_and_time, &stbuf->st_mtime);
548 if (entry->d_type == DT_DIR)
550 if (d->extension[0] != 0)
551 error_exit("Directory with extension %s", entry->d_name);
552 d->flags = DIRECTORY_FLAG;
555 d->flags = entry->d_name[0] == '.' ? HIDDEN_FLAG : 0;
556 d->size = stbuf->st_size;
557 d->next_in_directory = parent->first_record;
558 parent->first_record = d;
565 /*-----------------------------------------------------------------------------
566 This function compares two directory records according to the ISO9660 rules
567 for directory sorting and returns a negative value if p is before q, or a
568 positive value if p is after q.
569 -----------------------------------------------------------------------------*/
571 static int compare_directory_order(PDIR_RECORD p, PDIR_RECORD q)
573 int n = strcmp(p->name_on_cd, q->name_on_cd);
575 n = strcmp(p->extension_on_cd, q->extension_on_cd);
579 /*-----------------------------------------------------------------------------
580 This function compares two directory records (which must represent
581 directories) according to the ISO9660 rules for path table sorting and returns
582 a negative value if p is before q, or a positive vlaue if p is after q.
583 -----------------------------------------------------------------------------*/
585 static int compare_path_table_order(PDIR_RECORD p, PDIR_RECORD q)
587 int n = p->level - q->level;
592 n = compare_path_table_order(p->parent, q->parent);
594 n = compare_directory_order(p, q);
599 /*-----------------------------------------------------------------------------
600 This function appends the specified string to the buffer source[].
601 -----------------------------------------------------------------------------*/
603 static void append_string_to_source(char *s)
606 *end_source++ = *s++;
609 /*-----------------------------------------------------------------------------
610 This function scans all files from the current source[] (which must end in \,
611 and represents a directory already in the database as d),
612 and puts the appropriate directory records into the database in memory, with
613 the specified root. It calls itself recursively to scan all subdirectories.
614 -----------------------------------------------------------------------------*/
619 make_directory_records (PDIR_RECORD d)
622 struct _finddata_t f;
623 char *old_end_source;
626 d->first_record = NULL;
627 strcpy(end_source, "*.*");
629 findhandle =_findfirst(source, &f);
634 if ((f.attrib & (_A_HIDDEN | _A_SUBDIR)) == 0 && f.name[0] != '.')
636 if (strcmp(f.name, DIRECTORY_TIMESTAMP) == 0)
638 convert_date_and_time(&d->date_and_time, &f.time_create);
642 if (verbosity == VERBOSE)
644 old_end_source = end_source;
645 strcpy(end_source, f.name);
646 printf("%d: file %s\n", d->level, source);
647 end_source = old_end_source;
649 (void) new_directory_record(&f, d);
653 while (_findnext(findhandle, &f) == 0);
655 _findclose(findhandle);
658 strcpy(end_source, "*.*");
659 findhandle= _findfirst(source, &f);
664 if (f.attrib & _A_SUBDIR && f.name[0] != '.')
666 old_end_source = end_source;
667 append_string_to_source(f.name);
668 *end_source++ = DIR_SEPARATOR_CHAR;
669 if (verbosity == VERBOSE)
672 printf("%d: directory %s\n", d->level + 1, source);
674 if (d->level < MAX_LEVEL)
676 new_d = new_directory_record(&f, d);
677 new_d->next_in_path_table = root.next_in_path_table;
678 root.next_in_path_table = new_d;
679 new_d->level = d->level + 1;
680 make_directory_records(new_d);
684 error_exit("Directory is nested too deep");
686 end_source = old_end_source;
689 while (_findnext(findhandle, &f) == 0);
691 _findclose(findhandle);
695 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
702 make_directory_records (PDIR_RECORD d)
706 struct dirent *entry;
707 char *old_end_source;
711 d->first_record = NULL;
713 dirp = opendir(source);
717 while ((entry = readdir (dirp)) != NULL)
719 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
720 continue; // skip self and parent
722 if (entry->d_type == DT_REG) // normal file
724 // Check for an absolute path
725 if (source[0] == DIR_SEPARATOR_CHAR)
728 strcat(buf, DIR_SEPARATOR_STRING);
729 strcat(buf, entry->d_name);
733 getcwd(buf, sizeof(buf));
734 strcat(buf, DIR_SEPARATOR_STRING);
736 strcat(buf, entry->d_name);
738 if (stat(buf, &stbuf) == -1)
740 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
744 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
746 convert_date_and_time(&d->date_and_time, &stbuf.st_size);
750 if (verbosity == VERBOSE)
752 printf("%d: file %s\n", d->level, buf);
754 (void) new_directory_record(entry, &stbuf, d);
762 error_exit("Can't open %s\n", source);
766 dirp = opendir(source);
769 while ((entry = readdir (dirp)) != NULL)
771 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
772 continue; // skip self and parent
774 if (entry->d_type == DT_DIR) // directory
776 old_end_source = end_source;
777 append_string_to_source(entry->d_name);
778 *end_source++ = DIR_SEPARATOR_CHAR;
779 if (verbosity == VERBOSE)
782 printf("%d: directory %s\n", d->level + 1, source);
784 if (d->level < MAX_LEVEL)
786 // Check for an absolute path
787 if (source[0] == DIR_SEPARATOR_CHAR)
793 getcwd(buf, sizeof(buf));
794 strcat(buf, DIR_SEPARATOR_STRING);
797 if (stat(buf, &stbuf) == -1)
799 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
802 new_d = new_directory_record(entry, &stbuf, d);
803 new_d->next_in_path_table = root.next_in_path_table;
804 root.next_in_path_table = new_d;
805 new_d->level = d->level + 1;
806 make_directory_records(new_d);
810 error_exit("Directory is nested too deep");
812 end_source = old_end_source;
819 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
824 /*-----------------------------------------------------------------------------
825 This function loads the file specifications for the file or directory
826 corresponding to the specified directory record into the source[] buffer. It
828 -----------------------------------------------------------------------------*/
830 static void get_file_specifications(PDIR_RECORD d)
834 get_file_specifications(d->parent);
835 append_string_to_source(d->name);
836 if ((d->flags & DIRECTORY_FLAG) == 0 && d->extension[0] != 0)
839 append_string_to_source(d->extension);
841 if (d->flags & DIRECTORY_FLAG)
842 *end_source++ = DIR_SEPARATOR_CHAR;
846 static void pass(void)
853 unsigned int name_length;
855 DWORD number_of_sectors;
856 char *old_end_source;
860 // first 16 sectors are zeros
862 write_block(16 * SECTOR_SIZE, 0);
864 // Primary Volume Descriptor
866 write_string("\1CD001\1");
868 write_block(32, ' '); // system identifier
871 for (i = 0; i < 32; i++)
872 write_byte(*t != 0 ? toupper(*t++) : ' ');
875 write_both_endian_dword(total_sectors);
877 write_both_endian_word((WORD) 1); // volume set size
878 write_both_endian_word((WORD) 1); // volume sequence number
879 write_both_endian_word((WORD) 2048); // sector size
880 write_both_endian_dword(path_table_size);
881 write_little_endian_dword(little_endian_path_table_sector);
882 write_little_endian_dword((DWORD) 0); // second little endian path table
883 write_big_endian_dword(big_endian_path_table_sector);
884 write_big_endian_dword((DWORD) 0); // second big endian path table
885 write_directory_record(&root, DOT_RECORD);
886 write_block(128, ' '); // volume set identifier
887 write_block(128, ' '); // publisher identifier
888 write_block(128, ' '); // data preparer identifier
889 write_block(128, ' '); // application identifier
890 write_block(37, ' '); // copyright file identifier
891 write_block(37, ' '); // abstract file identifier
892 write_block(37, ' '); // bibliographic file identifier
893 write_string("0000000000000000"); // volume creation
895 write_string("0000000000000000"); // most recent modification
897 write_string("0000000000000000"); // volume expires
899 write_string("0000000000000000"); // volume is effective
906 // Boot Volume Descriptor
908 if (eltorito == TRUE)
911 write_string("CD001\1");
912 write_string("EL TORITO SPECIFICATION"); // identifier
913 write_block(9, 0); // padding
914 write_block(32, 0); // unused
915 write_little_endian_dword(boot_catalog_sector); // pointer to boot catalog
920 // Volume Descriptor Set Terminator
922 write_string("\377CD001\1");
928 if (eltorito == TRUE)
930 boot_catalog_sector = cd.sector;
934 write_byte(0); // x86 boot code
935 write_little_endian_word(0); // reserved
936 write_string("ReactOS Foundation");
937 write_block(6, 0); // padding
938 write_little_endian_word(0x62E); // checksum
939 write_little_endian_word(0xAA55); // signature
942 write_byte(0x88); // bootable
943 write_byte(0); // no emulation
944 write_big_endian_word(0); // load segment = default (0x07c0)
945 write_byte(0); // partition type
946 write_byte(0); // unused
947 write_little_endian_word(4); // sector count
948 write_little_endian_dword(boot_image_sector); // sector count
956 if (eltorito == TRUE)
958 boot_image_sector = cd.sector;
960 file = fopen(bootimage, "rb");
962 error_exit("Can't open %s\n", bootimage);
963 fseek(file, 0, SEEK_END);
965 fseek(file, 0, SEEK_SET);
968 n = BUFFER_SIZE - cd.count;
969 if ((DWORD) n > size)
971 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
974 error_exit("Read error in file %s\n", bootimage);
977 if (cd.count == BUFFER_SIZE)
979 cd.sector += n / SECTOR_SIZE;
980 cd.offset += n % SECTOR_SIZE;
988 // Little Endian Path Table
990 little_endian_path_table_sector = cd.sector;
992 write_byte(0); // number of sectors in extended attribute record
993 write_little_endian_dword(root.sector);
994 write_little_endian_word((WORD) 1);
999 root.path_table_index = 1;
1000 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1002 name_length = strlen(d->name_on_cd);
1003 write_byte(name_length);
1004 write_byte(0); // number of sectors in extended attribute record
1005 write_little_endian_dword(d->sector);
1006 write_little_endian_word(d->parent->path_table_index);
1007 write_string(d->name_on_cd);
1008 if (name_length & 1)
1010 d->path_table_index = ++index;
1013 path_table_size = (cd.sector - little_endian_path_table_sector) *
1014 SECTOR_SIZE + cd.offset;
1017 // Big Endian Path Table
1019 big_endian_path_table_sector = cd.sector;
1021 write_byte(0); // number of sectors in extended attribute record
1022 write_big_endian_dword(root.sector);
1023 write_big_endian_word((WORD) 1);
1027 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1029 name_length = strlen(d->name_on_cd);
1030 write_byte(name_length);
1031 write_byte(0); // number of sectors in extended attribute record
1032 write_big_endian_dword(d->sector);
1033 write_big_endian_word(d->parent->path_table_index);
1034 write_string(d->name_on_cd);
1035 if (name_length & 1)
1041 // directories and files
1043 for (d = &root; d != NULL; d = d->next_in_path_table)
1047 d->sector = cd.sector;
1048 write_directory_record(d, DOT_RECORD);
1049 write_directory_record(d == &root ? d : d->parent, DOT_DOT_RECORD);
1050 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1052 write_directory_record(q,
1053 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD);
1056 d->size = (cd.sector - d->sector) * SECTOR_SIZE;
1057 number_of_directories++;
1058 bytes_in_directories += d->size;
1062 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1064 if ((q->flags & DIRECTORY_FLAG) == 0)
1066 q->sector = cd.sector;
1068 if (cd.file == NULL)
1070 number_of_sectors = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
1071 cd.sector += number_of_sectors;
1073 bytes_in_files += size;
1074 unused_bytes_at_ends_of_files +=
1075 number_of_sectors * SECTOR_SIZE - size;
1079 old_end_source = end_source;
1080 get_file_specifications(q);
1082 if (verbosity == VERBOSE)
1083 printf("Writing %s\n", source);
1084 file = fopen(source, "rb");
1086 error_exit("Can't open %s\n", source);
1087 fseek(file, 0, SEEK_SET);
1090 n = BUFFER_SIZE - cd.count;
1091 if ((DWORD) n > size)
1093 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
1096 error_exit("Read error in file %s\n", source);
1099 if (cd.count == BUFFER_SIZE)
1101 cd.sector += n / SECTOR_SIZE;
1102 cd.offset += n % SECTOR_SIZE;
1106 end_source = old_end_source;
1113 total_sectors = (DWORD)cd.sector;
1116 static char HELP[] =
1117 "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] source volume image\n"
1119 " source specifications of base directory containing all files to\n"
1120 " be written to CD-ROM image\n"
1121 " volume volume label\n"
1122 " image image file or device\n"
1123 " -q quiet mode - display nothing but error messages\n"
1124 " -v verbose mode - display file information as files are\n"
1125 " scanned and written - overrides -p option\n"
1126 " -p show progress while writing\n"
1127 " -s N abort operation before beginning write if image will be\n"
1128 " larger than N megabytes (i.e. 1024*1024*N bytes)\n"
1129 " -m accept punctuation marks other than underscores in\n"
1130 " names and extensions\n"
1131 " -b bootimage create bootable ElTorito CD-ROM using 'no emulation' mode\n";
1133 /*-----------------------------------------------------------------------------
1134 Program execution starts here.
1135 -----------------------------------------------------------------------------*/
1137 int main(int argc, char **argv)
1139 BOOL q_option = FALSE;
1140 BOOL v_option = FALSE;
1153 // initialize root directory
1155 cd.buffer = malloc (BUFFER_SIZE);
1156 if (cd.buffer == NULL)
1157 error_exit("Insufficient memory");
1159 memset(&root, 0, sizeof(root));
1161 root.flags = DIRECTORY_FLAG;
1163 // initialize CD-ROM write buffer
1166 cd.filespecs[0] = 0;
1168 // initialize parameters
1172 show_progress = FALSE;
1173 accept_punctuation_marks = FALSE;
1175 volume_label[0] = 0;
1179 // scan command line arguments
1181 for (i = 1; i < argc; i++)
1183 if (memcmp(argv[i], "-s", 2) == 0)
1191 error_exit("Missing size limit parameter");
1194 size_limit = size_limit * 10 + *t++ - '0';
1195 if (size_limit < 1 || size_limit > 800)
1196 error_exit("Invalid size limit");
1197 size_limit <<= 9; // convert megabyte to sector count
1199 else if (strcmp(argv[i], "-q") == 0)
1201 else if (strcmp(argv[i], "-v") == 0)
1203 else if (strcmp(argv[i], "-p") == 0)
1204 show_progress = TRUE;
1205 else if (strcmp(argv[i], "-m") == 0)
1206 accept_punctuation_marks = TRUE;
1207 else if (strcmp(argv[i], "-b") == 0)
1209 strcpy(bootimage, argv[++i]);
1212 else if (i + 2 < argc)
1214 strcpy(source, argv[i++]);
1215 strncpy(volume_label, argv[i++], sizeof(volume_label) - 1);
1216 strcpy(cd.filespecs, argv[i]);
1219 error_exit("Missing command line argument");
1223 show_progress = FALSE;
1224 verbosity = VERBOSE;
1229 show_progress = FALSE;
1232 error_exit("Missing source directory");
1233 if (volume_label[0] == 0)
1234 error_exit("Missing volume label");
1235 if (cd.filespecs[0] == 0)
1236 error_exit("Missing image file specifications");
1239 // set source[] and end_source to source directory, with a terminating directory separator
1241 end_source = source + strlen(source);
1242 if (end_source[-1] == ':')
1243 *end_source++ = '.';
1244 if (end_source[-1] != DIR_SEPARATOR_CHAR)
1245 *end_source++ = DIR_SEPARATOR_CHAR;
1247 // scan all files and create directory structure in memory
1249 make_directory_records(&root);
1251 // sort path table entries
1253 root.next_in_path_table = sort_linked_list(root.next_in_path_table, 1,
1254 compare_path_table_order);
1256 // initialize CD-ROM write buffer
1263 // make non-writing pass over directory structure to obtain the proper
1264 // sector numbers and offsets and to determine the size of the image
1266 number_of_files = bytes_in_files = number_of_directories =
1267 bytes_in_directories = unused_bytes_at_ends_of_files =0;
1270 if (verbosity >= NORMAL)
1272 printf("%s bytes ", edit_with_commas(bytes_in_files, TRUE));
1273 printf("in %s files\n", edit_with_commas(number_of_files, FALSE));
1274 printf("%s unused bytes at ends of files\n",
1275 edit_with_commas(unused_bytes_at_ends_of_files, TRUE));
1276 printf("%s bytes ", edit_with_commas(bytes_in_directories, TRUE));
1277 printf("in %s directories\n",
1278 edit_with_commas(number_of_directories, FALSE));
1279 printf("%s other bytes\n", edit_with_commas(root.sector * SECTOR_SIZE, TRUE));
1280 puts("-------------");
1281 printf("%s total bytes\n",
1282 edit_with_commas(total_sectors * SECTOR_SIZE, TRUE));
1283 puts("=============");
1286 if (size_limit != 0 && total_sectors > size_limit)
1287 error_exit("Size limit exceeded");
1289 // re-initialize CD-ROM write buffer
1291 cd.file = fopen(cd.filespecs, "w+b");
1292 if (cd.file == NULL)
1293 error_exit("Can't open image file %s", cd.filespecs);
1299 // make writing pass over directory structure
1307 if (fclose(cd.file) != 0)
1310 error_exit("File write error in image file %s", cd.filespecs);
1313 if (verbosity >= NORMAL)
1314 puts("CD-ROM image made successfully");