3 * by Philip J. Erdelsky
5 * http://www.alumni.caltech.edu/~pje/
11 * by Casper S. Hornstrup
12 * chorns@users.sourceforge.net
18 * - No Joliet file name validations
19 * - Very bad ISO file name generation
22 * convert long filename to iso9660 file name by Magnus Olsen
23 * magnus@greatlord.com
25 * $Id: cdmake.c 58431 2013-03-05 08:07:21Z tkreuzer $
28 /* According to his website, this file was released into the public domain by Phillip J. Erdelsky */
40 # if defined(__FreeBSD__) || defined(__APPLE__)
44 # endif // __FreeBSD__
46 # include <sys/types.h>
57 #define DIR_SEPARATOR_CHAR '/'
58 #define DIR_SEPARATOR_STRING "/"
60 #define DIR_SEPARATOR_CHAR '\\'
61 #define DIR_SEPARATOR_STRING "\\"
64 typedef unsigned char BYTE
;
65 typedef unsigned short WORD
;
66 typedef unsigned long DWORD
;
72 // file system parameters
75 #define MAX_NAME_LENGTH 64
76 #define MAX_CDNAME_LENGTH 8
77 #define MAX_EXTENSION_LENGTH 10
78 #define MAX_CDEXTENSION_LENGTH 3
79 #define SECTOR_SIZE 2048
80 #define BUFFER_SIZE (8 * SECTOR_SIZE)
82 const BYTE HIDDEN_FLAG
= 1;
83 const BYTE DIRECTORY_FLAG
= 2;
89 DWORD sector
; // sector to receive next byte
90 int offset
; // offset of next byte in sector
91 int count
; // number of bytes in buffer
96 typedef struct date_and_time
104 } DATE_AND_TIME
, *PDATE_AND_TIME
;
106 typedef struct directory_record
108 struct directory_record
*next_in_directory
;
109 struct directory_record
*next_in_path_table
; /* directory record only */
110 struct directory_record
*next_in_memory
;
111 struct directory_record
*first_record
; /* directory record only */
112 struct directory_record
*parent
;
114 char name
[MAX_NAME_LENGTH
+1];
115 char name_on_cd
[MAX_CDNAME_LENGTH
+1];
116 char extension
[MAX_EXTENSION_LENGTH
+1];
117 char extension_on_cd
[MAX_CDEXTENSION_LENGTH
+1];
119 DATE_AND_TIME date_and_time
;
124 unsigned level
; /* directory record only */
125 WORD path_table_index
; /* directory record only */
126 } DIR_RECORD
, *PDIR_RECORD
;
129 typedef enum directory_record_type
135 } DIR_RECORD_TYPE
, *PDIR_RECORD_TYPE
;
138 PDIR_RECORD
sort_linked_list(PDIR_RECORD
,
139 unsigned, int (*)(PDIR_RECORD
, PDIR_RECORD
));
142 static char DIRECTORY_TIMESTAMP
[] = "~Y$'KOR$.3K&";
144 static jmp_buf error
;
145 static struct cd_image cd
;
147 char volume_label
[32];
151 enum {QUIET
, NORMAL
, VERBOSE
} verbosity
;
154 BOOL accept_punctuation_marks
;
157 DWORD path_table_size
;
158 DWORD little_endian_path_table_sector
;
159 DWORD big_endian_path_table_sector
;
160 DWORD number_of_files
;
161 DWORD bytes_in_files
;
162 DWORD unused_bytes_at_ends_of_files
;
163 DWORD number_of_directories
;
164 DWORD bytes_in_directories
;
168 DWORD boot_catalog_sector
;
169 DWORD boot_image_sector
;
170 WORD boot_image_size
; // counted in 512 byte sectors
173 DWORD joliet_path_table_size
;
174 DWORD joliet_little_endian_path_table_sector
;
175 DWORD joliet_big_endian_path_table_sector
;
177 /*-----------------------------------------------------------------------------
178 This function edits a 32-bit unsigned number into a comma-delimited form, such
179 as 4,294,967,295 for the largest possible number, and returns a pointer to a
180 static buffer containing the result. It suppresses leading zeros and commas,
181 but optionally pads the result with blanks at the left so the result is always
182 exactly 13 characters long (excluding the terminating zero).
184 CAUTION: A statement containing more than one call on this function, such as
185 printf("%s, %s", edit_with_commas(a), edit_with_commas(b)), will produce
186 incorrect results because all calls use the same static bufffer.
187 -----------------------------------------------------------------------------*/
189 static char *edit_with_commas(DWORD x
, BOOL pad
)
195 if (i
% 4 == 2) s
[--i
] = ',';
196 s
[--i
] = (char)(x
% 10 + '0');
197 } while ((x
/=10) != 0);
200 while (i
> 0) s
[--i
] = ' ';
205 /*-----------------------------------------------------------------------------
206 This function releases all allocated memory blocks.
207 -----------------------------------------------------------------------------*/
209 static void release_memory(void)
211 while (root
.next_in_memory
!= NULL
)
213 struct directory_record
*next
=
214 root
.next_in_memory
->next_in_memory
;
216 free (root
.joliet_name
);
217 free (root
.next_in_memory
);
218 root
.next_in_memory
= next
;
220 if (cd
.buffer
!= NULL
)
227 /*-----------------------------------------------------------------------------
228 This function edits and displays an error message and then jumps back to the
229 error exit point in main().
230 -----------------------------------------------------------------------------*/
232 void error_exit ( const char* fmt
, ... )
246 /*-----------------------------------------------------------------------------
247 This function, which is called only on the second pass, and only when the
248 buffer is not empty, flushes the buffer to the CD-ROM image.
249 -----------------------------------------------------------------------------*/
251 static void flush_buffer(void)
253 if (fwrite(cd
.buffer
, cd
.count
, 1, cd
.file
) < 1)
254 error_exit("File write error");
259 edit_with_commas((total_sectors
- cd
.sector
) * SECTOR_SIZE
, TRUE
));
263 /*-----------------------------------------------------------------------------
264 This function writes a single byte to the CD-ROM image. On the first pass (in
265 which cd.handle < 0), it does not actually write anything but merely updates
266 the file pointer as though the byte had been written.
267 -----------------------------------------------------------------------------*/
269 static void write_byte(BYTE x
)
273 cd
.buffer
[cd
.count
] = x
;
274 if (++cd
.count
== BUFFER_SIZE
)
277 if (++cd
.offset
== SECTOR_SIZE
)
284 /*-----------------------------------------------------------------------------
285 These functions write a word or double word to the CD-ROM image with the
287 -----------------------------------------------------------------------------*/
289 static void write_little_endian_word(WORD x
)
292 write_byte((BYTE
)(x
>> 8));
295 static void write_big_endian_word(WORD x
)
297 write_byte((BYTE
)(x
>> 8));
301 static void write_both_endian_word(WORD x
)
303 write_little_endian_word(x
);
304 write_big_endian_word(x
);
307 static void write_little_endian_dword(DWORD x
)
310 write_byte((BYTE
)(x
>> 8));
311 write_byte((BYTE
)(x
>> 16));
312 write_byte((BYTE
)(x
>> 24));
315 static void write_big_endian_dword(DWORD x
)
317 write_byte((BYTE
)(x
>> 24));
318 write_byte((BYTE
)(x
>> 16));
319 write_byte((BYTE
)(x
>> 8));
323 static void write_both_endian_dword(DWORD x
)
325 write_little_endian_dword(x
);
326 write_big_endian_dword(x
);
329 /*-----------------------------------------------------------------------------
330 This function writes enough zeros to fill out the end of a sector, and leaves
331 the file pointer at the beginning of the next sector. If the file pointer is
332 already at the beginning of a sector, it writes nothing.
333 -----------------------------------------------------------------------------*/
335 static void fill_sector(void)
337 while (cd
.offset
!= 0)
341 /*-----------------------------------------------------------------------------
342 This function writes a string to the CD-ROM image. The terminating \0 is not
344 -----------------------------------------------------------------------------*/
346 static void write_string(char *s
)
352 /*-----------------------------------------------------------------------------
353 This function writes a ansi string as a big endian unicode string to the CD-ROM
354 image. The terminating \0 is not written.
355 -----------------------------------------------------------------------------*/
357 static void write_string_as_big_endian_unicode(char *s
)
366 /*-----------------------------------------------------------------------------
367 This function writes a block of identical bytes to the CD-ROM image.
368 -----------------------------------------------------------------------------*/
370 static void write_block(unsigned count
, BYTE value
)
379 /*-----------------------------------------------------------------------------
380 This function writes a block of identical bige endian words to the CD-ROM image.
381 -----------------------------------------------------------------------------*/
383 static void write_word_block(unsigned count
, WORD value
)
387 write_big_endian_word(value
);
392 /*-----------------------------------------------------------------------------
393 This function writes a directory record to the CD_ROM image.
394 -----------------------------------------------------------------------------*/
397 write_directory_record(PDIR_RECORD d
,
398 DIR_RECORD_TYPE DirType
,
401 unsigned identifier_size
;
402 unsigned record_size
;
406 if (DirType
== DOT_RECORD
|| DirType
== DOT_DOT_RECORD
)
409 identifier_size
= strlen(d
->joliet_name
) * 2;
419 case SUBDIRECTORY_RECORD
:
420 /*printf ( "Subdir: %s\n", d->name_on_cd );*/
421 identifier_size
= strlen(d
->name_on_cd
);
424 /*printf ( "File: %s.%s -> %s.%s\n", d->name, d->extension, d->name_on_cd, d->extension_on_cd );*/
425 identifier_size
= strlen(d
->name_on_cd
) + 2;
426 if (d
->extension_on_cd
[0] != 0)
427 identifier_size
+= 1 + strlen(d
->extension_on_cd
);
434 record_size
= 33 + identifier_size
;
435 if ((identifier_size
& 1) == 0)
437 if (cd
.offset
+ record_size
> SECTOR_SIZE
)
439 write_byte((BYTE
)record_size
);
440 write_byte(0); // number of sectors in extended attribute record
443 write_both_endian_dword(d
->joliet_sector
);
444 write_both_endian_dword(d
->joliet_size
);
448 write_both_endian_dword(d
->sector
);
449 write_both_endian_dword(d
->size
);
451 write_byte((BYTE
)(d
->date_and_time
.year
- 1900));
452 write_byte(d
->date_and_time
.month
);
453 write_byte(d
->date_and_time
.day
);
454 write_byte(d
->date_and_time
.hour
);
455 write_byte(d
->date_and_time
.minute
);
456 write_byte(d
->date_and_time
.second
);
457 write_byte(0); // GMT offset
458 write_byte(d
->flags
);
459 write_byte(0); // file unit size for an interleaved file
460 write_byte(0); // interleave gap size for an interleaved file
461 write_both_endian_word((WORD
) 1); // volume sequence number
462 write_byte((BYTE
)identifier_size
);
471 case SUBDIRECTORY_RECORD
:
473 write_string_as_big_endian_unicode(d
->joliet_name
);
475 write_string(d
->name_on_cd
);
480 write_string_as_big_endian_unicode(d
->joliet_name
);
484 write_string(d
->name_on_cd
);
485 if (d
->extension_on_cd
[0] != 0)
488 write_string(d
->extension_on_cd
);
494 if ((identifier_size
& 1) == 0)
498 /*-----------------------------------------------------------------------------
499 This function converts the date and time words from an ffblk structure and
500 puts them into a date_and_time structure.
501 -----------------------------------------------------------------------------*/
503 static void convert_date_and_time(PDATE_AND_TIME dt
, time_t *time
)
507 timedef
= localtime(time
);
509 dt
->second
= timedef
->tm_sec
;
510 dt
->minute
= timedef
->tm_min
;
511 dt
->hour
= timedef
->tm_hour
;
512 dt
->day
= timedef
->tm_mday
;
513 dt
->month
= timedef
->tm_mon
+ 1;
514 dt
->year
= timedef
->tm_year
+ 1900;
517 /*-----------------------------------------------------------------------------
518 This function checks the specified character, if necessary, and
519 generates an error if it is a punctuation mark other than an underscore.
520 It also converts small letters to capital letters and returns the
522 -----------------------------------------------------------------------------*/
524 static int check_for_punctuation(int c
, const char *name
)
526 c
= toupper(c
& 0xFF);
527 if (!accept_punctuation_marks
&& !isalnum(c
) && c
!= '_')
528 error_exit("Punctuation mark in %s", name
);
532 #if defined(_WIN32) && !defined(strcasecmp)
533 #define strcasecmp stricmp
536 /*-----------------------------------------------------------------------------
537 This function checks to see if there's a cdname conflict.
538 -----------------------------------------------------------------------------*/
540 int cdname_exists ( PDIR_RECORD d
)
542 PDIR_RECORD p
= d
->parent
->first_record
;
546 && !strcasecmp ( p
->name_on_cd
, d
->name_on_cd
)
547 && !strcasecmp ( p
->extension_on_cd
, d
->extension_on_cd
) )
549 p
= p
->next_in_directory
;
554 void parse_filename_into_dirrecord ( const char* filename
, PDIR_RECORD d
, BOOL dir
)
556 const char *s
= filename
;
557 char *t
= d
->name_on_cd
;
560 int filename_counter
;
561 filename_counter
= 1;
570 if ( (size_t)(t
-d
->name_on_cd
) < sizeof(d
->name_on_cd
)-1 )
571 *t
++ = check_for_punctuation(*s
, filename
);
573 error_exit ("'%s' is not ISO-9660, aborting...", filename
);
574 if ( (size_t)(n
-d
->name
) < sizeof(d
->name
)-1 )
577 error_exit ( "'%s' is not ISO-9660, aborting...", filename
);
580 if (strlen(s
) > MAX_EXTENSION_LENGTH
)
582 error_exit ( "'%s' has too long extension for cdmake, aborting...", filename
);
585 strcpy(d
->extension
, s
);
586 t
= d
->extension_on_cd
;
589 if ( (size_t)(t
-d
->extension_on_cd
) < sizeof(d
->extension_on_cd
)-1 )
590 *t
++ = check_for_punctuation(*s
, filename
);
592 error_exit ( "'%s' is not ISO-9660, aborting...", filename
);
600 if (d
->extension
[0] != 0)
603 error_exit("Directory with extension %s", filename
);
605 d
->flags
= DIRECTORY_FLAG
;
610 filename_counter
= 1;
611 while ( cdname_exists ( d
) )
614 // the file name must be least 8 char long
615 if (strlen(d
->name_on_cd
)<8)
616 error_exit ( "'%s' is a duplicate file name, aborting...", filename
);
618 if ((d
->name_on_cd
[8] == '.') && (strlen(d
->name_on_cd
) < 13))
619 error_exit ( "'%s' is a duplicate file name, aborting...", filename
);
621 // max 255 times for equal short filename
622 if (filename_counter
>255) error_exit ( "'%s' is a duplicate file name, aborting...", filename
);
623 d
->name_on_cd
[8] = '~';
624 memset(&d
->name_on_cd
[9],0,5);
625 sprintf(&d
->name_on_cd
[9],"%d",filename_counter
);
632 joliet_length
= strlen(filename
);
633 if (joliet_length
> 64)
634 error_exit ( "'%s' is not Joliet, aborting...", filename
);
635 d
->joliet_name
= malloc(joliet_length
+ 1);
636 if (d
->joliet_name
== NULL
)
637 error_exit("Insufficient memory");
638 strcpy(d
->joliet_name
, filename
);
642 /*-----------------------------------------------------------------------------
643 This function creates a new directory record with the information from the
644 specified ffblk. It links it into the beginning of the directory list
645 for the specified parent and returns a pointer to the new record.
646 -----------------------------------------------------------------------------*/
652 new_directory_record (struct _finddata_t
*f
,
657 d
= malloc(sizeof(DIR_RECORD
));
659 error_exit("Insufficient memory");
660 memset ( d
, 0, sizeof(DIR_RECORD
) );
661 d
->next_in_memory
= root
.next_in_memory
;
662 root
.next_in_memory
= d
;
664 /* I need the parent set before calling parse_filename_into_dirrecord(),
665 because that functions checks for duplicate file names*/
667 parse_filename_into_dirrecord ( f
->name
, d
, f
->attrib
& _A_SUBDIR
);
669 convert_date_and_time(&d
->date_and_time
, &f
->time_write
);
670 d
->flags
|= f
->attrib
& _A_HIDDEN
? HIDDEN_FLAG
: 0;
671 d
->size
= d
->joliet_size
= f
->size
;
672 d
->next_in_directory
= parent
->first_record
;
673 parent
->first_record
= d
;
681 new_directory_record (struct dirent
*entry
,
692 d
= malloc(sizeof(DIR_RECORD
));
694 error_exit("Insufficient memory");
695 memset ( d
, 0, sizeof(DIR_RECORD
) );
696 d
->next_in_memory
= root
.next_in_memory
;
697 root
.next_in_memory
= d
;
699 /* I need the parent set before calling parse_filename_into_dirrecord(),
700 because that functions checks for duplicate file names*/
703 parse_filename_into_dirrecord ( entry
->d_name
, d
, entry
->d_type
== DT_DIR
);
705 parse_filename_into_dirrecord ( entry
->d_name
, d
, S_ISDIR(stbuf
->st_mode
) );
708 convert_date_and_time(&d
->date_and_time
, &stbuf
->st_mtime
);
709 d
->flags
|= entry
->d_name
[0] == '.' ? HIDDEN_FLAG
: 0;
710 d
->size
= d
->joliet_size
= stbuf
->st_size
;
711 d
->next_in_directory
= parent
->first_record
;
712 parent
->first_record
= d
;
718 /*-----------------------------------------------------------------------------
719 This function compares two directory records according to the ISO9660 rules
720 for directory sorting and returns a negative value if p is before q, or a
721 positive value if p is after q.
722 -----------------------------------------------------------------------------*/
724 static int compare_directory_order(PDIR_RECORD p
, PDIR_RECORD q
)
726 int n
= strcmp(p
->name_on_cd
, q
->name_on_cd
);
728 n
= strcmp(p
->extension_on_cd
, q
->extension_on_cd
);
732 /*-----------------------------------------------------------------------------
733 This function compares two directory records (which must represent
734 directories) according to the ISO9660 rules for path table sorting and returns
735 a negative value if p is before q, or a positive vlaue if p is after q.
736 -----------------------------------------------------------------------------*/
738 static int compare_path_table_order(PDIR_RECORD p
, PDIR_RECORD q
)
740 int n
= p
->level
- q
->level
;
745 n
= compare_path_table_order(p
->parent
, q
->parent
);
747 n
= compare_directory_order(p
, q
);
752 /*-----------------------------------------------------------------------------
753 This function appends the specified string to the buffer source[].
754 -----------------------------------------------------------------------------*/
756 static void append_string_to_source(char *s
)
759 *end_source
++ = *s
++;
762 /*-----------------------------------------------------------------------------
763 This function scans all files from the current source[] (which must end in \,
764 and represents a directory already in the database as d),
765 and puts the appropriate directory records into the database in memory, with
766 the specified root. It calls itself recursively to scan all subdirectories.
767 -----------------------------------------------------------------------------*/
772 make_directory_records (PDIR_RECORD d
)
775 struct _finddata_t f
;
776 char *old_end_source
;
779 d
->first_record
= NULL
;
780 strcpy(end_source
, "*.*");
782 findhandle
=_findfirst(source
, &f
);
787 if ((f
.attrib
& (_A_HIDDEN
| _A_SUBDIR
)) == 0 && f
.name
[0] != '.')
789 if (strcmp(f
.name
, DIRECTORY_TIMESTAMP
) == 0)
791 convert_date_and_time(&d
->date_and_time
, &f
.time_write
);
795 if (verbosity
== VERBOSE
)
797 old_end_source
= end_source
;
798 strcpy(end_source
, f
.name
);
799 printf("%d: file %s\n", d
->level
, source
);
800 end_source
= old_end_source
;
802 (void) new_directory_record(&f
, d
);
806 while (_findnext(findhandle
, &f
) == 0);
808 _findclose(findhandle
);
811 strcpy(end_source
, "*.*");
812 findhandle
= _findfirst(source
, &f
);
817 if (f
.attrib
& _A_SUBDIR
&& f
.name
[0] != '.')
819 old_end_source
= end_source
;
820 append_string_to_source(f
.name
);
821 *end_source
++ = DIR_SEPARATOR_CHAR
;
822 if (verbosity
== VERBOSE
)
825 printf("%d: directory %s\n", d
->level
+ 1, source
);
827 if (d
->level
< MAX_LEVEL
)
829 new_d
= new_directory_record(&f
, d
);
830 new_d
->next_in_path_table
= root
.next_in_path_table
;
831 root
.next_in_path_table
= new_d
;
832 new_d
->level
= d
->level
+ 1;
833 make_directory_records(new_d
);
837 error_exit("Directory is nested too deep");
839 end_source
= old_end_source
;
842 while (_findnext(findhandle
, &f
) == 0);
844 _findclose(findhandle
);
848 d
->first_record
= sort_linked_list(d
->first_record
, 0, compare_directory_order
);
855 make_directory_records (PDIR_RECORD d
)
859 struct dirent
*entry
;
860 char *old_end_source
;
864 d
->first_record
= NULL
;
867 dirp
= opendir(source
);
870 while ((entry
= readdir (dirp
)) != NULL
)
872 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
873 continue; // skip self and parent
875 if (entry
->d_type
== DT_REG
) // normal file
877 // Check for an absolute path
878 if (source
[0] == DIR_SEPARATOR_CHAR
)
881 strcat(buf
, DIR_SEPARATOR_STRING
);
882 strcat(buf
, entry
->d_name
);
886 if (!getcwd(buf
, sizeof(buf
)))
887 error_exit("Can't get CWD: %s\n", strerror(errno
));
888 strcat(buf
, DIR_SEPARATOR_STRING
);
890 strcat(buf
, entry
->d_name
);
893 if (stat(buf
, &stbuf
) == -1)
895 error_exit("Can't access '%s' (%s)\n", buf
, strerror(errno
));
899 if (strcmp(entry
->d_name
, DIRECTORY_TIMESTAMP
) == 0)
901 convert_date_and_time(&d
->date_and_time
, &stbuf
.st_ctime
);
905 if (verbosity
== VERBOSE
)
907 printf("%d: file %s\n", d
->level
, buf
);
909 (void) new_directory_record(entry
, &stbuf
, d
);
917 error_exit("Can't open %s\n", source
);
921 dirp
= opendir(source
);
924 while ((entry
= readdir (dirp
)) != NULL
)
926 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
927 continue; // skip self and parent
929 if (entry
->d_type
== DT_DIR
) // directory
931 old_end_source
= end_source
;
932 append_string_to_source(entry
->d_name
);
933 *end_source
++ = DIR_SEPARATOR_CHAR
;
935 if (verbosity
== VERBOSE
)
937 printf("%d: directory %s\n", d
->level
+ 1, source
);
939 if (d
->level
< MAX_LEVEL
)
941 // Check for an absolute path
942 if (source
[0] == DIR_SEPARATOR_CHAR
)
948 if (!getcwd(buf
, sizeof(buf
)))
949 error_exit("Can't get CWD: %s\n", strerror(errno
));
950 strcat(buf
, DIR_SEPARATOR_STRING
);
954 if (stat(buf
, &stbuf
) == -1)
956 error_exit("Can't access '%s' (%s)\n", buf
, strerror(errno
));
959 new_d
= new_directory_record(entry
, &stbuf
, d
);
960 new_d
->next_in_path_table
= root
.next_in_path_table
;
961 root
.next_in_path_table
= new_d
;
962 new_d
->level
= d
->level
+ 1;
963 make_directory_records(new_d
);
967 error_exit("Directory is nested too deep");
969 end_source
= old_end_source
;
977 error_exit("Can't open %s\n", source
);
983 dirp
= opendir(source
);
986 while ((entry
= readdir (dirp
)) != NULL
)
988 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
989 continue; // skip self and parent
991 // Check for an absolute path
992 if (source
[0] == DIR_SEPARATOR_CHAR
)
995 strcat(buf
, DIR_SEPARATOR_STRING
);
996 strcat(buf
, entry
->d_name
);
1000 if (!getcwd(buf
, sizeof(buf
)))
1001 error_exit("Can't get CWD: %s\n", strerror(errno
));
1002 strcat(buf
, DIR_SEPARATOR_STRING
);
1003 strcat(buf
, source
);
1004 strcat(buf
, entry
->d_name
);
1007 if (stat(buf
, &stbuf
) == -1)
1009 error_exit("Can't access '%s' (%s)\n", buf
, strerror(errno
));
1013 if (S_ISDIR(stbuf
.st_mode
))
1015 old_end_source
= end_source
;
1016 append_string_to_source(entry
->d_name
);
1017 *end_source
++ = DIR_SEPARATOR_CHAR
;
1019 if (verbosity
== VERBOSE
)
1021 printf("%d: directory %s\n", d
->level
+ 1, source
);
1024 if (d
->level
< MAX_LEVEL
)
1026 new_d
= new_directory_record(entry
, &stbuf
, d
);
1027 new_d
->next_in_path_table
= root
.next_in_path_table
;
1028 root
.next_in_path_table
= new_d
;
1029 new_d
->level
= d
->level
+ 1;
1030 make_directory_records(new_d
);
1034 error_exit("Directory is nested too deep");
1037 end_source
= old_end_source
;
1040 else if (S_ISREG(stbuf
.st_mode
))
1042 if (strcmp(entry
->d_name
, DIRECTORY_TIMESTAMP
) == 0)
1044 convert_date_and_time(&d
->date_and_time
, &stbuf
.st_ctime
);
1048 if (verbosity
== VERBOSE
)
1050 printf("%d: file %s\n", d
->level
, buf
);
1052 (void) new_directory_record(entry
, &stbuf
, d
);
1060 error_exit("Can't open %s\n", source
);
1067 d
->first_record
= sort_linked_list(d
->first_record
, 0, compare_directory_order
);
1072 /*-----------------------------------------------------------------------------
1073 This function loads the file specifications for the file or directory
1074 corresponding to the specified directory record into the source[] buffer. It
1076 -----------------------------------------------------------------------------*/
1078 static void get_file_specifications(PDIR_RECORD d
)
1082 get_file_specifications(d
->parent
);
1083 if (d
->joliet_name
== NULL
)
1084 append_string_to_source(d
->name
);
1086 append_string_to_source(d
->joliet_name
);
1088 if (((d
->flags
& DIRECTORY_FLAG
) == 0 || joliet
) && d
->extension
[0] != 0)
1090 if (d
->joliet_name
== NULL
)
1092 *end_source
++ = '.';
1093 append_string_to_source(d
->extension
);
1096 if (d
->flags
& DIRECTORY_FLAG
)
1097 *end_source
++ = DIR_SEPARATOR_CHAR
;
1101 static void pass(void)
1108 unsigned int name_length
;
1110 DWORD number_of_sectors
;
1111 char *old_end_source
;
1115 // first 16 sectors are zeros
1117 write_block(16 * SECTOR_SIZE
, 0);
1119 // Primary Volume Descriptor
1121 write_string("\1CD001\1");
1123 write_block(32, ' '); // system identifier
1126 for (i
= 0; i
< 32; i
++)
1127 write_byte( (BYTE
)( (*t
!= 0) ? toupper(*t
++) : ' ' ) );
1130 write_both_endian_dword(total_sectors
);
1132 write_both_endian_word((WORD
) 1); // volume set size
1133 write_both_endian_word((WORD
) 1); // volume sequence number
1134 write_both_endian_word((WORD
) 2048); // sector size
1135 write_both_endian_dword(path_table_size
);
1136 write_little_endian_dword(little_endian_path_table_sector
);
1137 write_little_endian_dword((DWORD
) 0); // second little endian path table
1138 write_big_endian_dword(big_endian_path_table_sector
);
1139 write_big_endian_dword((DWORD
) 0); // second big endian path table
1140 write_directory_record(&root
, DOT_RECORD
, FALSE
);
1141 write_block(128, ' '); // volume set identifier
1142 write_block(128, ' '); // publisher identifier
1143 write_block(128, ' '); // data preparer identifier
1144 write_block(128, ' '); // application identifier
1145 write_block(37, ' '); // copyright file identifier
1146 write_block(37, ' '); // abstract file identifier
1147 write_block(37, ' '); // bibliographic file identifier
1148 write_string("0000000000000000"); // volume creation
1150 write_string("0000000000000000"); // most recent modification
1152 write_string("0000000000000000"); // volume expires
1154 write_string("0000000000000000"); // volume is effective
1161 // Boot Volume Descriptor
1163 if (eltorito
== TRUE
)
1166 write_string("CD001\1");
1167 write_string("EL TORITO SPECIFICATION"); // identifier
1168 write_block(9, 0); // padding
1169 write_block(32, 0); // unused
1170 write_little_endian_dword(boot_catalog_sector
); // pointer to boot catalog
1174 // Supplementary Volume Descriptor
1178 write_string("\2CD001\1");
1181 write_word_block(16, L
' '); // system identifier
1184 for (i
= 0; i
< 16; i
++)
1185 write_big_endian_word( (BYTE
)( (*t
!= 0) ? *t
++ : ' ' ) );
1188 write_both_endian_dword(total_sectors
);
1189 write_string("%/E");
1191 write_both_endian_word((WORD
) 1); // volume set size
1192 write_both_endian_word((WORD
) 1); // volume sequence number
1193 write_both_endian_word((WORD
) 2048); // sector size
1194 write_both_endian_dword(joliet_path_table_size
);
1195 write_little_endian_dword(joliet_little_endian_path_table_sector
);
1196 write_little_endian_dword((DWORD
) 0); // second little endian path table
1197 write_big_endian_dword(joliet_big_endian_path_table_sector
);
1198 write_big_endian_dword((DWORD
) 0); // second big endian path table
1199 write_directory_record(&root
, DOT_RECORD
, TRUE
);
1200 write_word_block(64, ' '); // volume set identifier
1201 write_word_block(64, ' '); // publisher identifier
1202 write_word_block(64, ' '); // data preparer identifier
1203 write_word_block(64, ' '); // application identifier
1204 write_block(37, ' '); // copyright file identifier
1205 write_block(37, ' '); // abstract file identifier
1206 write_block(37, ' '); // bibliographic file identifier
1207 write_string("0000000000000000"); // volume creation
1209 write_string("0000000000000000"); // most recent modification
1211 write_string("0000000000000000"); // volume expires
1213 write_string("0000000000000000"); // volume is effective
1221 // Volume Descriptor Set Terminator
1222 write_string("\377CD001\1");
1226 if (eltorito
== TRUE
)
1228 boot_catalog_sector
= cd
.sector
;
1232 write_byte(0); // x86 boot code
1233 write_little_endian_word(0); // reserved
1234 write_string("ReactOS Foundation");
1235 write_block(6, 0); // padding
1236 write_little_endian_word(0x62E); // checksum
1237 write_little_endian_word(0xAA55); // signature
1240 write_byte(0x88); // bootable
1241 write_byte(0); // no emulation
1242 write_big_endian_word(0); // load segment = default (0x07c0)
1243 write_byte(0); // partition type
1244 write_byte(0); // unused
1245 write_little_endian_word(boot_image_size
); // sector count
1246 write_little_endian_dword(boot_image_sector
); // sector
1252 if (eltorito
== TRUE
)
1254 boot_image_sector
= cd
.sector
;
1256 file
= fopen(bootimage
, "rb");
1258 error_exit("Can't open %s\n", bootimage
);
1259 fseek(file
, 0, SEEK_END
);
1261 fseek(file
, 0, SEEK_SET
);
1262 if (size
== 0 || (size
% 2048))
1265 error_exit("Invalid boot image size (%lu bytes)\n", size
);
1267 boot_image_size
= size
/ 512;
1270 n
= BUFFER_SIZE
- cd
.count
;
1271 if ((DWORD
) n
> size
)
1273 if (fread (cd
.buffer
+ cd
.count
, n
, 1, file
) < 1)
1276 error_exit("Read error in file %s\n", bootimage
);
1279 if (cd
.count
== BUFFER_SIZE
)
1281 cd
.sector
+= n
/ SECTOR_SIZE
;
1282 cd
.offset
+= n
% SECTOR_SIZE
;
1289 // Little Endian Path Table
1290 little_endian_path_table_sector
= cd
.sector
;
1292 write_byte(0); // number of sectors in extended attribute record
1293 write_little_endian_dword(root
.sector
);
1294 write_little_endian_word((WORD
) 1);
1299 root
.path_table_index
= 1;
1300 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
1302 name_length
= strlen(d
->name_on_cd
);
1303 write_byte((BYTE
)name_length
);
1304 write_byte(0); // number of sectors in extended attribute record
1305 write_little_endian_dword(d
->sector
);
1306 write_little_endian_word(d
->parent
->path_table_index
);
1307 write_string(d
->name_on_cd
);
1308 if (name_length
& 1)
1310 d
->path_table_index
= ++index
;
1313 path_table_size
= (cd
.sector
- little_endian_path_table_sector
) *
1314 SECTOR_SIZE
+ cd
.offset
;
1317 // Big Endian Path Table
1319 big_endian_path_table_sector
= cd
.sector
;
1321 write_byte(0); // number of sectors in extended attribute record
1322 write_big_endian_dword(root
.sector
);
1323 write_big_endian_word((WORD
) 1);
1327 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
1329 name_length
= strlen(d
->name_on_cd
);
1330 write_byte((BYTE
)name_length
);
1331 write_byte(0); // number of sectors in extended attribute record
1332 write_big_endian_dword(d
->sector
);
1333 write_big_endian_word(d
->parent
->path_table_index
);
1334 write_string(d
->name_on_cd
);
1335 if (name_length
& 1)
1342 // Little Endian Path Table
1344 joliet_little_endian_path_table_sector
= cd
.sector
;
1346 write_byte(0); // number of sectors in extended attribute record
1347 write_little_endian_dword(root
.joliet_sector
);
1348 write_little_endian_word((WORD
) 1);
1352 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
1354 name_length
= strlen(d
->joliet_name
) * 2;
1355 write_byte((BYTE
)name_length
);
1356 write_byte(0); // number of sectors in extended attribute record
1357 write_little_endian_dword(d
->joliet_sector
);
1358 write_little_endian_word(d
->parent
->path_table_index
);
1359 write_string_as_big_endian_unicode(d
->joliet_name
);
1362 joliet_path_table_size
= (cd
.sector
- joliet_little_endian_path_table_sector
) *
1363 SECTOR_SIZE
+ cd
.offset
;
1366 // Big Endian Path Table
1368 joliet_big_endian_path_table_sector
= cd
.sector
;
1370 write_byte(0); // number of sectors in extended attribute record
1371 write_big_endian_dword(root
.joliet_sector
);
1372 write_big_endian_word((WORD
) 1);
1376 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
1378 name_length
= strlen(d
->joliet_name
) * 2;
1379 write_byte((BYTE
)name_length
);
1380 write_byte(0); // number of sectors in extended attribute record
1381 write_big_endian_dword(d
->joliet_sector
);
1382 write_big_endian_word(d
->parent
->path_table_index
);
1383 write_string_as_big_endian_unicode(d
->joliet_name
);
1388 // directories and files
1389 for (d
= &root
; d
!= NULL
; d
= d
->next_in_path_table
)
1392 d
->sector
= cd
.sector
;
1393 write_directory_record(d
, DOT_RECORD
, FALSE
);
1394 write_directory_record(d
== &root
? d
: d
->parent
, DOT_DOT_RECORD
, FALSE
);
1395 for (q
= d
->first_record
; q
!= NULL
; q
= q
->next_in_directory
)
1397 write_directory_record(q
,
1398 q
->flags
& DIRECTORY_FLAG
? SUBDIRECTORY_RECORD
: FILE_RECORD
,
1402 d
->size
= (cd
.sector
- d
->sector
) * SECTOR_SIZE
;
1404 // write directory for joliet
1407 d
->joliet_sector
= cd
.sector
;
1408 write_directory_record(d
, DOT_RECORD
, TRUE
);
1409 write_directory_record(d
== &root
? d
: d
->parent
, DOT_DOT_RECORD
, TRUE
);
1410 for (q
= d
->first_record
; q
!= NULL
; q
= q
->next_in_directory
)
1412 write_directory_record(q
,
1413 q
->flags
& DIRECTORY_FLAG
? SUBDIRECTORY_RECORD
: FILE_RECORD
,
1417 d
->joliet_size
= (cd
.sector
- d
->joliet_sector
) * SECTOR_SIZE
;
1418 bytes_in_directories
+= d
->joliet_size
;
1421 number_of_directories
++;
1422 bytes_in_directories
+= d
->size
;
1425 for (q
= d
->first_record
; q
!= NULL
; q
= q
->next_in_directory
)
1427 if ((q
->flags
& DIRECTORY_FLAG
) == 0)
1429 q
->sector
= q
->joliet_sector
= cd
.sector
;
1431 if (cd
.file
== NULL
)
1433 number_of_sectors
= (size
+ SECTOR_SIZE
- 1) / SECTOR_SIZE
;
1434 cd
.sector
+= number_of_sectors
;
1436 bytes_in_files
+= size
;
1437 unused_bytes_at_ends_of_files
+=
1438 number_of_sectors
* SECTOR_SIZE
- size
;
1442 old_end_source
= end_source
;
1443 get_file_specifications(q
);
1445 if (verbosity
== VERBOSE
)
1446 printf("Writing %s\n", source
);
1447 file
= fopen(source
, "rb");
1449 error_exit("Can't open %s\n", source
);
1450 fseek(file
, 0, SEEK_SET
);
1453 n
= BUFFER_SIZE
- cd
.count
;
1454 if ((DWORD
) n
> size
)
1456 if (fread (cd
.buffer
+ cd
.count
, n
, 1, file
) < 1)
1459 error_exit("Read error in file %s\n", source
);
1462 if (cd
.count
== BUFFER_SIZE
)
1464 cd
.sector
+= n
/ SECTOR_SIZE
;
1465 cd
.offset
+= n
% SECTOR_SIZE
;
1469 end_source
= old_end_source
;
1476 total_sectors
= (DWORD
)cd
.sector
;
1479 static char HELP
[] =
1480 "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] [-j] source volume image\n"
1482 " source specifications of base directory containing all files to\n"
1483 " be written to CD-ROM image\n"
1484 " volume volume label\n"
1485 " image image file or device\n"
1486 " -q quiet mode - display nothing but error messages\n"
1487 " -v verbose mode - display file information as files are\n"
1488 " scanned and written - overrides -p option\n"
1489 " -p show progress while writing\n"
1490 " -s N abort operation before beginning write if image will be\n"
1491 " larger than N megabytes (i.e. 1024*1024*N bytes)\n"
1492 " -m accept punctuation marks other than underscores in\n"
1493 " names and extensions\n"
1494 " -b bootimage create bootable ElTorito CD-ROM using 'no emulation' mode\n"
1495 " -j generate Joliet filename records\n";
1497 /*-----------------------------------------------------------------------------
1498 Program execution starts here.
1499 -----------------------------------------------------------------------------*/
1501 int main(int argc
, char **argv
)
1503 BOOL q_option
= FALSE
;
1504 BOOL v_option
= FALSE
;
1517 // initialize root directory
1519 cd
.buffer
= malloc (BUFFER_SIZE
);
1520 if (cd
.buffer
== NULL
)
1521 error_exit("Insufficient memory");
1523 memset(&root
, 0, sizeof(root
));
1525 root
.flags
= DIRECTORY_FLAG
;
1527 // initialize CD-ROM write buffer
1530 cd
.filespecs
[0] = 0;
1532 // initialize parameters
1536 show_progress
= FALSE
;
1537 accept_punctuation_marks
= FALSE
;
1539 volume_label
[0] = 0;
1543 // scan command line arguments
1545 for (i
= 1; i
< argc
; i
++)
1547 if (memcmp(argv
[i
], "-s", 2) == 0)
1555 error_exit("Missing size limit parameter");
1558 size_limit
= size_limit
* 10 + *t
++ - '0';
1559 if (size_limit
< 1 || size_limit
> 800)
1560 error_exit("Invalid size limit");
1561 size_limit
<<= 9; // convert megabyte to sector count
1563 else if (strcmp(argv
[i
], "-q") == 0)
1565 else if (strcmp(argv
[i
], "-v") == 0)
1567 else if (strcmp(argv
[i
], "-p") == 0)
1568 show_progress
= TRUE
;
1569 else if (strcmp(argv
[i
], "-m") == 0)
1570 accept_punctuation_marks
= TRUE
;
1571 else if (strcmp(argv
[i
], "-j") == 0)
1573 else if (strcmp(argv
[i
], "-b") == 0)
1575 strcpy(bootimage
, argv
[++i
]);
1578 else if (i
+ 2 < argc
)
1580 strcpy(source
, argv
[i
++]);
1581 strncpy(volume_label
, argv
[i
++], sizeof(volume_label
) - 1);
1582 strcpy(cd
.filespecs
, argv
[i
]);
1585 error_exit("Missing command line argument");
1589 show_progress
= FALSE
;
1590 verbosity
= VERBOSE
;
1595 show_progress
= FALSE
;
1598 error_exit("Missing source directory");
1599 if (volume_label
[0] == 0)
1600 error_exit("Missing volume label");
1601 if (cd
.filespecs
[0] == 0)
1602 error_exit("Missing image file specifications");
1605 // set source[] and end_source to source directory, with a terminating directory separator
1607 end_source
= source
+ strlen(source
);
1608 if (end_source
[-1] == ':')
1609 *end_source
++ = '.';
1610 if (end_source
[-1] != DIR_SEPARATOR_CHAR
)
1611 *end_source
++ = DIR_SEPARATOR_CHAR
;
1613 // scan all files and create directory structure in memory
1615 make_directory_records(&root
);
1617 // sort path table entries
1619 root
.next_in_path_table
= sort_linked_list(root
.next_in_path_table
, 1,
1620 compare_path_table_order
);
1622 // initialize CD-ROM write buffer
1629 // make non-writing pass over directory structure to obtain the proper
1630 // sector numbers and offsets and to determine the size of the image
1632 number_of_files
= bytes_in_files
= number_of_directories
=
1633 bytes_in_directories
= unused_bytes_at_ends_of_files
=0;
1636 if (verbosity
>= NORMAL
)
1638 printf("%s bytes ", edit_with_commas(bytes_in_files
, TRUE
));
1639 printf("in %s files\n", edit_with_commas(number_of_files
, FALSE
));
1640 printf("%s unused bytes at ends of files\n",
1641 edit_with_commas(unused_bytes_at_ends_of_files
, TRUE
));
1642 printf("%s bytes ", edit_with_commas(bytes_in_directories
, TRUE
));
1643 printf("in %s directories\n",
1644 edit_with_commas(number_of_directories
, FALSE
));
1645 printf("%s other bytes\n", edit_with_commas(root
.sector
* SECTOR_SIZE
, TRUE
));
1646 puts("-------------");
1647 printf("%s total bytes\n",
1648 edit_with_commas(total_sectors
* SECTOR_SIZE
, TRUE
));
1649 puts("=============");
1652 if (size_limit
!= 0 && total_sectors
> size_limit
)
1653 error_exit("Size limit exceeded");
1655 // re-initialize CD-ROM write buffer
1657 cd
.file
= fopen(cd
.filespecs
, "w+b");
1658 if (cd
.file
== NULL
)
1659 error_exit("Can't open image file %s", cd
.filespecs
);
1665 // make writing pass over directory structure
1673 if (fclose(cd
.file
) != 0)
1676 error_exit("File write error in image file %s", cd
.filespecs
);
1679 if (verbosity
>= NORMAL
)
1680 puts("CD-ROM image made successfully");