3 * by Philip J. Erdelsky
5 * http://www.alumni.caltech.edu/~pje/
12 * by Casper S. Hornstrup
13 * chorns@users.sourceforge.net
19 * - No Joliet file name validations
20 * - Very bad ISO file name generation
23 * convert long filename to iso9660 file name by Magnus Olsen
24 * magnus@greatlord.com
29 /* According to his website, this file was released into the public domain by Phillip J. Erdelsky */
45 # endif // __FreeBSD__
47 # include <sys/types.h>
58 #define DIR_SEPARATOR_CHAR '/'
59 #define DIR_SEPARATOR_STRING "/"
61 #define DIR_SEPARATOR_CHAR '\\'
62 #define DIR_SEPARATOR_STRING "\\"
65 typedef unsigned char BYTE
;
66 typedef unsigned short WORD
;
67 typedef unsigned long DWORD
;
73 // file system parameters
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)
83 const BYTE HIDDEN_FLAG
= 1;
84 const BYTE DIRECTORY_FLAG
= 2;
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
97 typedef struct date_and_time
105 } DATE_AND_TIME
, *PDATE_AND_TIME
;
107 typedef struct directory_record
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
;
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];
120 DATE_AND_TIME date_and_time
;
125 unsigned level
; /* directory record only */
126 WORD path_table_index
; /* directory record only */
127 } DIR_RECORD
, *PDIR_RECORD
;
130 typedef enum directory_record_type
136 } DIR_RECORD_TYPE
, *PDIR_RECORD_TYPE
;
139 PDIR_RECORD
sort_linked_list(PDIR_RECORD
,
140 unsigned, int (*)(PDIR_RECORD
, PDIR_RECORD
));
143 static char DIRECTORY_TIMESTAMP
[] = "~Y$'KOR$.3K&";
145 static jmp_buf error
;
146 static struct cd_image cd
;
148 char volume_label
[32];
152 enum {QUIET
, NORMAL
, VERBOSE
} verbosity
;
155 BOOL accept_punctuation_marks
;
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
;
169 DWORD boot_catalog_sector
;
170 DWORD boot_image_sector
;
171 WORD boot_image_size
; // counted in 512 byte sectors
174 DWORD joliet_path_table_size
;
175 DWORD joliet_little_endian_path_table_sector
;
176 DWORD joliet_big_endian_path_table_sector
;
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).
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 -----------------------------------------------------------------------------*/
190 static char *edit_with_commas(DWORD x
, BOOL pad
)
196 if (i
% 4 == 2) s
[--i
] = ',';
197 s
[--i
] = (char)(x
% 10 + '0');
198 } while ((x
/=10) != 0);
201 while (i
> 0) s
[--i
] = ' ';
206 /*-----------------------------------------------------------------------------
207 This function releases all allocated memory blocks.
208 -----------------------------------------------------------------------------*/
210 static void release_memory(void)
212 while (root
.next_in_memory
!= NULL
)
214 struct directory_record
*next
=
215 root
.next_in_memory
->next_in_memory
;
217 free (root
.joliet_name
);
218 free (root
.next_in_memory
);
219 root
.next_in_memory
= next
;
221 if (cd
.buffer
!= NULL
)
228 /*-----------------------------------------------------------------------------
229 This function edits and displays an error message and then jumps back to the
230 error exit point in main().
231 -----------------------------------------------------------------------------*/
233 void error_exit ( const char* fmt
, ... )
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 -----------------------------------------------------------------------------*/
252 static void flush_buffer(void)
254 if (fwrite(cd
.buffer
, cd
.count
, 1, cd
.file
) < 1)
255 error_exit("File write error");
260 edit_with_commas((total_sectors
- cd
.sector
) * SECTOR_SIZE
, TRUE
));
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 -----------------------------------------------------------------------------*/
270 static void write_byte(BYTE x
)
274 cd
.buffer
[cd
.count
] = x
;
275 if (++cd
.count
== BUFFER_SIZE
)
278 if (++cd
.offset
== SECTOR_SIZE
)
285 /*-----------------------------------------------------------------------------
286 These functions write a word or double word to the CD-ROM image with the
288 -----------------------------------------------------------------------------*/
290 static void write_little_endian_word(WORD x
)
293 write_byte((BYTE
)(x
>> 8));
296 static void write_big_endian_word(WORD x
)
298 write_byte((BYTE
)(x
>> 8));
302 static void write_both_endian_word(WORD x
)
304 write_little_endian_word(x
);
305 write_big_endian_word(x
);
308 static void write_little_endian_dword(DWORD x
)
311 write_byte((BYTE
)(x
>> 8));
312 write_byte((BYTE
)(x
>> 16));
313 write_byte((BYTE
)(x
>> 24));
316 static void write_big_endian_dword(DWORD x
)
318 write_byte((BYTE
)(x
>> 24));
319 write_byte((BYTE
)(x
>> 16));
320 write_byte((BYTE
)(x
>> 8));
324 static void write_both_endian_dword(DWORD x
)
326 write_little_endian_dword(x
);
327 write_big_endian_dword(x
);
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 -----------------------------------------------------------------------------*/
336 static void fill_sector(void)
338 while (cd
.offset
!= 0)
342 /*-----------------------------------------------------------------------------
343 This function writes a string to the CD-ROM image. The terminating \0 is not
345 -----------------------------------------------------------------------------*/
347 static void write_string(char *s
)
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 -----------------------------------------------------------------------------*/
358 static void write_string_as_big_endian_unicode(char *s
)
367 /*-----------------------------------------------------------------------------
368 This function writes a block of identical bytes to the CD-ROM image.
369 -----------------------------------------------------------------------------*/
371 static void write_block(unsigned count
, BYTE value
)
380 /*-----------------------------------------------------------------------------
381 This function writes a block of identical bige endian words to the CD-ROM image.
382 -----------------------------------------------------------------------------*/
384 static void write_word_block(unsigned count
, WORD value
)
388 write_big_endian_word(value
);
393 /*-----------------------------------------------------------------------------
394 This function writes a directory record to the CD_ROM image.
395 -----------------------------------------------------------------------------*/
398 write_directory_record(PDIR_RECORD d
,
399 DIR_RECORD_TYPE DirType
,
402 unsigned identifier_size
;
403 unsigned record_size
;
407 if (DirType
== DOT_RECORD
|| DirType
== DOT_DOT_RECORD
)
410 identifier_size
= strlen(d
->joliet_name
) * 2;
420 case SUBDIRECTORY_RECORD
:
421 /*printf ( "Subdir: %s\n", d->name_on_cd );*/
422 identifier_size
= strlen(d
->name_on_cd
);
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
);
435 record_size
= 33 + identifier_size
;
436 if ((identifier_size
& 1) == 0)
438 if (cd
.offset
+ record_size
> SECTOR_SIZE
)
440 write_byte((BYTE
)record_size
);
441 write_byte(0); // number of sectors in extended attribute record
444 write_both_endian_dword(d
->joliet_sector
);
445 write_both_endian_dword(d
->joliet_size
);
449 write_both_endian_dword(d
->sector
);
450 write_both_endian_dword(d
->size
);
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
);
472 case SUBDIRECTORY_RECORD
:
474 write_string_as_big_endian_unicode(d
->joliet_name
);
476 write_string(d
->name_on_cd
);
481 write_string_as_big_endian_unicode(d
->joliet_name
);
485 write_string(d
->name_on_cd
);
486 if (d
->extension_on_cd
[0] != 0)
489 write_string(d
->extension_on_cd
);
495 if ((identifier_size
& 1) == 0)
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 -----------------------------------------------------------------------------*/
504 static void convert_date_and_time(PDATE_AND_TIME dt
, time_t *time
)
508 timedef
= localtime(time
);
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;
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
523 -----------------------------------------------------------------------------*/
525 static int check_for_punctuation(int c
, const char *name
)
527 c
= toupper(c
& 0xFF);
528 if (!accept_punctuation_marks
&& !isalnum(c
) && c
!= '_')
529 error_exit("Punctuation mark in %s", name
);
534 #define strcasecmp stricmp
537 /*-----------------------------------------------------------------------------
538 This function checks to see if there's a cdname conflict.
539 -----------------------------------------------------------------------------*/
541 int cdname_exists ( PDIR_RECORD d
)
543 PDIR_RECORD p
= d
->parent
->first_record
;
547 && !strcasecmp ( p
->name_on_cd
, d
->name_on_cd
)
548 && !strcasecmp ( p
->extension_on_cd
, d
->extension_on_cd
) )
550 p
= p
->next_in_directory
;
555 void parse_filename_into_dirrecord ( const char* filename
, PDIR_RECORD d
, BOOL dir
)
557 const char *s
= filename
;
558 char *t
= d
->name_on_cd
;
561 int filename_counter
;
562 filename_counter
= 1;
571 if ( (t
-d
->name_on_cd
) < sizeof(d
->name_on_cd
)-1 )
572 *t
++ = check_for_punctuation(*s
, filename
);
574 error_exit ("'%s' is not ISO-9660, aborting...", filename
);
575 if ( (n
-d
->name
) < sizeof(d
->name
)-1 )
578 error_exit ( "'%s' is not ISO-9660, aborting...", filename
);
581 if (strlen(s
) > MAX_EXTENSION_LENGTH
)
583 error_exit ( "'%s' has too long extension for cdmake, aborting...", filename
);
586 strcpy(d
->extension
, s
);
587 t
= d
->extension_on_cd
;
590 if ( (t
-d
->extension_on_cd
) < (sizeof(d
->extension_on_cd
)-1) )
591 *t
++ = check_for_punctuation(*s
, filename
);
593 error_exit ( "'%s' is not ISO-9660, aborting...", filename
);
601 if (d
->extension
[0] != 0)
604 d
->extension_on_cd
[0] = 0;
606 error_exit("Directory with extension %s", filename
);
608 d
->flags
= DIRECTORY_FLAG
;
613 filename_counter
= 1;
614 while ( cdname_exists ( d
) )
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
);
621 if ((d
->name_on_cd
[8] == '.') && (strlen(d
->name_on_cd
) < 13))
622 error_exit ( "'%s' is a duplicate file name, aborting...", filename
);
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
);
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
);
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 -----------------------------------------------------------------------------*/
653 new_directory_record (struct _finddata_t
*f
,
658 d
= malloc(sizeof(DIR_RECORD
));
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
;
665 /* I need the parent set before calling parse_filename_into_dirrecord(),
666 because that functions checks for duplicate file names*/
668 parse_filename_into_dirrecord ( f
->name
, d
, f
->attrib
& _A_SUBDIR
);
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
;
682 new_directory_record (struct dirent
*entry
,
693 d
= malloc(sizeof(DIR_RECORD
));
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
;
700 /* I need the parent set before calling parse_filename_into_dirrecord(),
701 because that functions checks for duplicate file names*/
704 parse_filename_into_dirrecord ( entry
->d_name
, d
, entry
->d_type
== DT_DIR
);
706 parse_filename_into_dirrecord ( entry
->d_name
, d
, S_ISDIR(stbuf
->st_mode
) );
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
;
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 -----------------------------------------------------------------------------*/
725 static int compare_directory_order(PDIR_RECORD p
, PDIR_RECORD q
)
727 int n
= strcmp(p
->name_on_cd
, q
->name_on_cd
);
729 n
= strcmp(p
->extension_on_cd
, q
->extension_on_cd
);
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 -----------------------------------------------------------------------------*/
739 static int compare_path_table_order(PDIR_RECORD p
, PDIR_RECORD q
)
741 int n
= p
->level
- q
->level
;
746 n
= compare_path_table_order(p
->parent
, q
->parent
);
748 n
= compare_directory_order(p
, q
);
753 /*-----------------------------------------------------------------------------
754 This function appends the specified string to the buffer source[].
755 -----------------------------------------------------------------------------*/
757 static void append_string_to_source(char *s
)
760 *end_source
++ = *s
++;
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 -----------------------------------------------------------------------------*/
773 make_directory_records (PDIR_RECORD d
)
776 struct _finddata_t f
;
777 char *old_end_source
;
780 d
->first_record
= NULL
;
781 strcpy(end_source
, "*.*");
783 findhandle
=_findfirst(source
, &f
);
788 if ((f
.attrib
& (_A_HIDDEN
| _A_SUBDIR
)) == 0 && f
.name
[0] != '.')
790 if (strcmp(f
.name
, DIRECTORY_TIMESTAMP
) == 0)
792 convert_date_and_time(&d
->date_and_time
, &f
.time_write
);
796 if (verbosity
== VERBOSE
)
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
;
803 (void) new_directory_record(&f
, d
);
807 while (_findnext(findhandle
, &f
) == 0);
809 _findclose(findhandle
);
812 strcpy(end_source
, "*.*");
813 findhandle
= _findfirst(source
, &f
);
818 if (f
.attrib
& _A_SUBDIR
&& f
.name
[0] != '.')
820 old_end_source
= end_source
;
821 append_string_to_source(f
.name
);
822 *end_source
++ = DIR_SEPARATOR_CHAR
;
823 if (verbosity
== VERBOSE
)
826 printf("%d: directory %s\n", d
->level
+ 1, source
);
828 if (d
->level
< MAX_LEVEL
)
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
);
838 error_exit("Directory is nested too deep");
840 end_source
= old_end_source
;
843 while (_findnext(findhandle
, &f
) == 0);
845 _findclose(findhandle
);
849 d
->first_record
= sort_linked_list(d
->first_record
, 0, compare_directory_order
);
856 make_directory_records (PDIR_RECORD d
)
860 struct dirent
*entry
;
861 char *old_end_source
;
865 d
->first_record
= NULL
;
868 dirp
= opendir(source
);
871 while ((entry
= readdir (dirp
)) != NULL
)
873 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
874 continue; // skip self and parent
876 if (entry
->d_type
== DT_REG
) // normal file
878 // Check for an absolute path
879 if (source
[0] == DIR_SEPARATOR_CHAR
)
882 strcat(buf
, DIR_SEPARATOR_STRING
);
883 strcat(buf
, entry
->d_name
);
887 getcwd(buf
, sizeof(buf
));
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 getcwd(buf
, sizeof(buf
));
949 strcat(buf
, DIR_SEPARATOR_STRING
);
953 if (stat(buf
, &stbuf
) == -1)
955 error_exit("Can't access '%s' (%s)\n", buf
, strerror(errno
));
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
);
966 error_exit("Directory is nested too deep");
968 end_source
= old_end_source
;
976 error_exit("Can't open %s\n", source
);
982 dirp
= opendir(source
);
985 while ((entry
= readdir (dirp
)) != NULL
)
987 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
988 continue; // skip self and parent
990 // Check for an absolute path
991 if (source
[0] == DIR_SEPARATOR_CHAR
)
994 strcat(buf
, DIR_SEPARATOR_STRING
);
995 strcat(buf
, entry
->d_name
);
999 getcwd(buf
, sizeof(buf
));
1000 strcat(buf
, DIR_SEPARATOR_STRING
);
1001 strcat(buf
, source
);
1002 strcat(buf
, entry
->d_name
);
1005 if (stat(buf
, &stbuf
) == -1)
1007 error_exit("Can't access '%s' (%s)\n", buf
, strerror(errno
));
1011 if (S_ISDIR(stbuf
.st_mode
))
1013 old_end_source
= end_source
;
1014 append_string_to_source(entry
->d_name
);
1015 *end_source
++ = DIR_SEPARATOR_CHAR
;
1017 if (verbosity
== VERBOSE
)
1019 printf("%d: directory %s\n", d
->level
+ 1, source
);
1022 if (d
->level
< MAX_LEVEL
)
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
);
1032 error_exit("Directory is nested too deep");
1035 end_source
= old_end_source
;
1038 else if (S_ISREG(stbuf
.st_mode
))
1040 if (strcmp(entry
->d_name
, DIRECTORY_TIMESTAMP
) == 0)
1042 convert_date_and_time(&d
->date_and_time
, &stbuf
.st_ctime
);
1046 if (verbosity
== VERBOSE
)
1048 printf("%d: file %s\n", d
->level
, buf
);
1050 (void) new_directory_record(entry
, &stbuf
, d
);
1058 error_exit("Can't open %s\n", source
);
1065 d
->first_record
= sort_linked_list(d
->first_record
, 0, compare_directory_order
);
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
1074 -----------------------------------------------------------------------------*/
1076 static void get_file_specifications(PDIR_RECORD d
)
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)
1084 *end_source
++ = '.';
1085 append_string_to_source(d
->extension
);
1087 if (d
->flags
& DIRECTORY_FLAG
)
1088 *end_source
++ = DIR_SEPARATOR_CHAR
;
1092 static void pass(void)
1099 unsigned int name_length
;
1101 DWORD number_of_sectors
;
1102 char *old_end_source
;
1106 // first 16 sectors are zeros
1108 write_block(16 * SECTOR_SIZE
, 0);
1110 // Primary Volume Descriptor
1112 write_string("\1CD001\1");
1114 write_block(32, ' '); // system identifier
1117 for (i
= 0; i
< 32; i
++)
1118 write_byte( (BYTE
)( (*t
!= 0) ? toupper(*t
++) : ' ' ) );
1121 write_both_endian_dword(total_sectors
);
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
1141 write_string("0000000000000000"); // most recent modification
1143 write_string("0000000000000000"); // volume expires
1145 write_string("0000000000000000"); // volume is effective
1152 // Boot Volume Descriptor
1154 if (eltorito
== TRUE
)
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
1165 // Supplementary Volume Descriptor
1169 write_string("\2CD001\1");
1172 write_word_block(16, L
' '); // system identifier
1175 for (i
= 0; i
< 16; i
++)
1176 write_big_endian_word( (BYTE
)( (*t
!= 0) ? *t
++ : ' ' ) );
1179 write_both_endian_dword(total_sectors
);
1180 write_string("%/E");
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
1200 write_string("0000000000000000"); // most recent modification
1202 write_string("0000000000000000"); // volume expires
1204 write_string("0000000000000000"); // volume is effective
1212 // Volume Descriptor Set Terminator
1213 write_string("\377CD001\1");
1217 if (eltorito
== TRUE
)
1219 boot_catalog_sector
= cd
.sector
;
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
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
1243 if (eltorito
== TRUE
)
1245 boot_image_sector
= cd
.sector
;
1247 file
= fopen(bootimage
, "rb");
1249 error_exit("Can't open %s\n", bootimage
);
1250 fseek(file
, 0, SEEK_END
);
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;
1258 n
= BUFFER_SIZE
- cd
.count
;
1259 if ((DWORD
) n
> size
)
1261 if (fread (cd
.buffer
+ cd
.count
, n
, 1, file
) < 1)
1264 error_exit("Read error in file %s\n", bootimage
);
1267 if (cd
.count
== BUFFER_SIZE
)
1269 cd
.sector
+= n
/ SECTOR_SIZE
;
1270 cd
.offset
+= n
% SECTOR_SIZE
;
1277 // Little Endian Path Table
1278 little_endian_path_table_sector
= cd
.sector
;
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);
1287 root
.path_table_index
= 1;
1288 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
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)
1298 d
->path_table_index
= ++index
;
1301 path_table_size
= (cd
.sector
- little_endian_path_table_sector
) *
1302 SECTOR_SIZE
+ cd
.offset
;
1305 // Big Endian Path Table
1307 big_endian_path_table_sector
= cd
.sector
;
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);
1315 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
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)
1330 // Little Endian Path Table
1332 joliet_little_endian_path_table_sector
= cd
.sector
;
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);
1340 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
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
);
1350 joliet_path_table_size
= (cd
.sector
- joliet_little_endian_path_table_sector
) *
1351 SECTOR_SIZE
+ cd
.offset
;
1354 // Big Endian Path Table
1356 joliet_big_endian_path_table_sector
= cd
.sector
;
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);
1364 for (d
= root
.next_in_path_table
; d
!= NULL
; d
= d
->next_in_path_table
)
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
);
1376 // directories and files
1377 for (d
= &root
; d
!= NULL
; d
= d
->next_in_path_table
)
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
)
1385 write_directory_record(q
,
1386 q
->flags
& DIRECTORY_FLAG
? SUBDIRECTORY_RECORD
: FILE_RECORD
,
1390 d
->size
= (cd
.sector
- d
->sector
) * SECTOR_SIZE
;
1392 // write directory for joliet
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
)
1400 write_directory_record(q
,
1401 q
->flags
& DIRECTORY_FLAG
? SUBDIRECTORY_RECORD
: FILE_RECORD
,
1405 d
->joliet_size
= (cd
.sector
- d
->joliet_sector
) * SECTOR_SIZE
;
1406 bytes_in_directories
+= d
->joliet_size
;
1409 number_of_directories
++;
1410 bytes_in_directories
+= d
->size
;
1413 for (q
= d
->first_record
; q
!= NULL
; q
= q
->next_in_directory
)
1415 if ((q
->flags
& DIRECTORY_FLAG
) == 0)
1417 q
->sector
= q
->joliet_sector
= cd
.sector
;
1419 if (cd
.file
== NULL
)
1421 number_of_sectors
= (size
+ SECTOR_SIZE
- 1) / SECTOR_SIZE
;
1422 cd
.sector
+= number_of_sectors
;
1424 bytes_in_files
+= size
;
1425 unused_bytes_at_ends_of_files
+=
1426 number_of_sectors
* SECTOR_SIZE
- size
;
1430 old_end_source
= end_source
;
1431 get_file_specifications(q
);
1433 if (verbosity
== VERBOSE
)
1434 printf("Writing %s\n", source
);
1435 file
= fopen(source
, "rb");
1437 error_exit("Can't open %s\n", source
);
1438 fseek(file
, 0, SEEK_SET
);
1441 n
= BUFFER_SIZE
- cd
.count
;
1442 if ((DWORD
) n
> size
)
1444 if (fread (cd
.buffer
+ cd
.count
, n
, 1, file
) < 1)
1447 error_exit("Read error in file %s\n", source
);
1450 if (cd
.count
== BUFFER_SIZE
)
1452 cd
.sector
+= n
/ SECTOR_SIZE
;
1453 cd
.offset
+= n
% SECTOR_SIZE
;
1457 end_source
= old_end_source
;
1464 total_sectors
= (DWORD
)cd
.sector
;
1467 static char HELP
[] =
1468 "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] [-j] source volume image\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";
1485 /*-----------------------------------------------------------------------------
1486 Program execution starts here.
1487 -----------------------------------------------------------------------------*/
1489 int main(int argc
, char **argv
)
1491 BOOL q_option
= FALSE
;
1492 BOOL v_option
= FALSE
;
1505 // initialize root directory
1507 cd
.buffer
= malloc (BUFFER_SIZE
);
1508 if (cd
.buffer
== NULL
)
1509 error_exit("Insufficient memory");
1511 memset(&root
, 0, sizeof(root
));
1513 root
.flags
= DIRECTORY_FLAG
;
1515 // initialize CD-ROM write buffer
1518 cd
.filespecs
[0] = 0;
1520 // initialize parameters
1524 show_progress
= FALSE
;
1525 accept_punctuation_marks
= FALSE
;
1527 volume_label
[0] = 0;
1531 // scan command line arguments
1533 for (i
= 1; i
< argc
; i
++)
1535 if (memcmp(argv
[i
], "-s", 2) == 0)
1543 error_exit("Missing size limit parameter");
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
1551 else if (strcmp(argv
[i
], "-q") == 0)
1553 else if (strcmp(argv
[i
], "-v") == 0)
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)
1561 else if (strcmp(argv
[i
], "-b") == 0)
1563 strcpy(bootimage
, argv
[++i
]);
1566 else if (i
+ 2 < argc
)
1568 strcpy(source
, argv
[i
++]);
1569 strncpy(volume_label
, argv
[i
++], sizeof(volume_label
) - 1);
1570 strcpy(cd
.filespecs
, argv
[i
]);
1573 error_exit("Missing command line argument");
1577 show_progress
= FALSE
;
1578 verbosity
= VERBOSE
;
1583 show_progress
= FALSE
;
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");
1593 // set source[] and end_source to source directory, with a terminating directory separator
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
;
1601 // scan all files and create directory structure in memory
1603 make_directory_records(&root
);
1605 // sort path table entries
1607 root
.next_in_path_table
= sort_linked_list(root
.next_in_path_table
, 1,
1608 compare_path_table_order
);
1610 // initialize CD-ROM write buffer
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
1620 number_of_files
= bytes_in_files
= number_of_directories
=
1621 bytes_in_directories
= unused_bytes_at_ends_of_files
=0;
1624 if (verbosity
>= NORMAL
)
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("=============");
1640 if (size_limit
!= 0 && total_sectors
> size_limit
)
1641 error_exit("Size limit exceeded");
1643 // re-initialize CD-ROM write buffer
1645 cd
.file
= fopen(cd
.filespecs
, "w+b");
1646 if (cd
.file
== NULL
)
1647 error_exit("Can't open image file %s", cd
.filespecs
);
1653 // make writing pass over directory structure
1661 if (fclose(cd
.file
) != 0)
1664 error_exit("File write error in image file %s", cd
.filespecs
);
1667 if (verbosity
>= NORMAL
)
1668 puts("CD-ROM image made successfully");