- Remove ALL the unneeded "author date id revision" svn properties.
[reactos.git] / reactos / tools / cdmake / cdmake.c
1 /*
2 * CD-ROM Maker
3 * by Philip J. Erdelsky
4 * pje@acm.org
5 * http://www.alumni.caltech.edu/~pje/
6 *
7 * ElTorito-Support
8 * by Eric Kohl
9 *
10 * Linux port
11 * by Casper S. Hornstrup
12 * chorns@users.sourceforge.net
13 *
14 * Joliet support
15 * by Filip Navara
16 * xnavara@volny.cz
17 * Limitations:
18 * - No Joliet file name validations
19 * - Very bad ISO file name generation
20 *
21 *
22 * convert long filename to iso9660 file name by Magnus Olsen
23 * magnus@greatlord.com
24 *
25 * $Id: cdmake.c 58431 2013-03-05 08:07:21Z tkreuzer $
26 */
27
28 /* According to his website, this file was released into the public domain by Phillip J. Erdelsky */
29
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #ifdef _WIN32
37 # include <io.h>
38 # include <dos.h>
39 #else
40 # if defined(__FreeBSD__) || defined(__APPLE__)
41 # include <sys/uio.h>
42 # else
43 # include <sys/io.h>
44 # endif // __FreeBSD__
45 # include <errno.h>
46 # include <sys/types.h>
47 # include <dirent.h>
48 # include <unistd.h>
49 #endif // _WIN32
50 #include <ctype.h>
51 #include <setjmp.h>
52 #include <time.h>
53 #ifndef _WIN32
54 #ifndef MAX_PATH
55 #define MAX_PATH 260
56 #endif
57 #define DIR_SEPARATOR_CHAR '/'
58 #define DIR_SEPARATOR_STRING "/"
59 #else
60 #define DIR_SEPARATOR_CHAR '\\'
61 #define DIR_SEPARATOR_STRING "\\"
62 #endif
63
64 typedef unsigned char BYTE;
65 typedef unsigned short WORD;
66 typedef unsigned long DWORD;
67 typedef int BOOL;
68
69 const BOOL TRUE = 1;
70 const BOOL FALSE = 0;
71
72 // file system parameters
73
74 #define MAX_LEVEL 8
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)
81
82 const BYTE HIDDEN_FLAG = 1;
83 const BYTE DIRECTORY_FLAG = 2;
84
85
86 struct cd_image
87 {
88 FILE *file;
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
92 char filespecs[128];
93 BYTE *buffer;
94 };
95
96 typedef struct date_and_time
97 {
98 BYTE second;
99 BYTE minute;
100 BYTE hour;
101 BYTE day;
102 BYTE month;
103 WORD year;
104 } DATE_AND_TIME, *PDATE_AND_TIME;
105
106 typedef struct directory_record
107 {
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;
113 BYTE flags;
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];
118 char *joliet_name;
119 DATE_AND_TIME date_and_time;
120 DWORD sector;
121 DWORD size;
122 DWORD joliet_sector;
123 DWORD joliet_size;
124 unsigned level; /* directory record only */
125 WORD path_table_index; /* directory record only */
126 } DIR_RECORD, *PDIR_RECORD;
127
128
129 typedef enum directory_record_type
130 {
131 DOT_RECORD,
132 DOT_DOT_RECORD,
133 SUBDIRECTORY_RECORD,
134 FILE_RECORD
135 } DIR_RECORD_TYPE, *PDIR_RECORD_TYPE;
136
137
138 PDIR_RECORD sort_linked_list(PDIR_RECORD,
139 unsigned, int (*)(PDIR_RECORD, PDIR_RECORD));
140
141
142 static char DIRECTORY_TIMESTAMP[] = "~Y$'KOR$.3K&";
143
144 static jmp_buf error;
145 static struct cd_image cd;
146
147 char volume_label[32];
148 DIR_RECORD root;
149 char source[512];
150 char *end_source;
151 enum {QUIET, NORMAL, VERBOSE} verbosity;
152 BOOL show_progress;
153 DWORD size_limit;
154 BOOL accept_punctuation_marks;
155
156 DWORD total_sectors;
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;
165
166 char bootimage[512];
167 BOOL eltorito;
168 DWORD boot_catalog_sector;
169 DWORD boot_image_sector;
170 WORD boot_image_size; // counted in 512 byte sectors
171
172 BOOL joliet;
173 DWORD joliet_path_table_size;
174 DWORD joliet_little_endian_path_table_sector;
175 DWORD joliet_big_endian_path_table_sector;
176
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).
183
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 -----------------------------------------------------------------------------*/
188
189 static char *edit_with_commas(DWORD x, BOOL pad)
190 {
191 static char s[14];
192 unsigned i = 13;
193 do
194 {
195 if (i % 4 == 2) s[--i] = ',';
196 s[--i] = (char)(x % 10 + '0');
197 } while ((x/=10) != 0);
198 if (pad)
199 {
200 while (i > 0) s[--i] = ' ';
201 }
202 return s + i;
203 }
204
205 /*-----------------------------------------------------------------------------
206 This function releases all allocated memory blocks.
207 -----------------------------------------------------------------------------*/
208
209 static void release_memory(void)
210 {
211 while (root.next_in_memory != NULL)
212 {
213 struct directory_record *next =
214 root.next_in_memory->next_in_memory;
215 if (joliet)
216 free (root.joliet_name);
217 free (root.next_in_memory);
218 root.next_in_memory = next;
219 }
220 if (cd.buffer != NULL)
221 {
222 free (cd.buffer);
223 cd.buffer = NULL;
224 }
225 }
226
227 /*-----------------------------------------------------------------------------
228 This function edits and displays an error message and then jumps back to the
229 error exit point in main().
230 -----------------------------------------------------------------------------*/
231
232 void error_exit ( const char* fmt, ... )
233 {
234 va_list arg;
235
236 va_start(arg, fmt);
237 vprintf(fmt, arg);
238 va_end(arg);
239 printf("\n");
240 if (cd.file != NULL)
241 fclose(cd.file);
242 release_memory();
243 exit(1);
244 }
245
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 -----------------------------------------------------------------------------*/
250
251 static void flush_buffer(void)
252 {
253 if (fwrite(cd.buffer, cd.count, 1, cd.file) < 1)
254 error_exit("File write error");
255 cd.count = 0;
256 if (show_progress)
257 {
258 printf("\r%s ",
259 edit_with_commas((total_sectors - cd.sector) * SECTOR_SIZE, TRUE));
260 }
261 }
262
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 -----------------------------------------------------------------------------*/
268
269 static void write_byte(BYTE x)
270 {
271 if (cd.file != NULL)
272 {
273 cd.buffer[cd.count] = x;
274 if (++cd.count == BUFFER_SIZE)
275 flush_buffer();
276 }
277 if (++cd.offset == SECTOR_SIZE)
278 {
279 cd.sector++;
280 cd.offset = 0;
281 }
282 }
283
284 /*-----------------------------------------------------------------------------
285 These functions write a word or double word to the CD-ROM image with the
286 specified endianity.
287 -----------------------------------------------------------------------------*/
288
289 static void write_little_endian_word(WORD x)
290 {
291 write_byte((BYTE)x);
292 write_byte((BYTE)(x >> 8));
293 }
294
295 static void write_big_endian_word(WORD x)
296 {
297 write_byte((BYTE)(x >> 8));
298 write_byte((BYTE)x);
299 }
300
301 static void write_both_endian_word(WORD x)
302 {
303 write_little_endian_word(x);
304 write_big_endian_word(x);
305 }
306
307 static void write_little_endian_dword(DWORD x)
308 {
309 write_byte((BYTE)x);
310 write_byte((BYTE)(x >> 8));
311 write_byte((BYTE)(x >> 16));
312 write_byte((BYTE)(x >> 24));
313 }
314
315 static void write_big_endian_dword(DWORD x)
316 {
317 write_byte((BYTE)(x >> 24));
318 write_byte((BYTE)(x >> 16));
319 write_byte((BYTE)(x >> 8));
320 write_byte((BYTE)x);
321 }
322
323 static void write_both_endian_dword(DWORD x)
324 {
325 write_little_endian_dword(x);
326 write_big_endian_dword(x);
327 }
328
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 -----------------------------------------------------------------------------*/
334
335 static void fill_sector(void)
336 {
337 while (cd.offset != 0)
338 write_byte(0);
339 }
340
341 /*-----------------------------------------------------------------------------
342 This function writes a string to the CD-ROM image. The terminating \0 is not
343 written.
344 -----------------------------------------------------------------------------*/
345
346 static void write_string(char *s)
347 {
348 while (*s != 0)
349 write_byte(*s++);
350 }
351
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 -----------------------------------------------------------------------------*/
356
357 static void write_string_as_big_endian_unicode(char *s)
358 {
359 while (*s != 0)
360 {
361 write_byte(0);
362 write_byte(*s++);
363 }
364 }
365
366 /*-----------------------------------------------------------------------------
367 This function writes a block of identical bytes to the CD-ROM image.
368 -----------------------------------------------------------------------------*/
369
370 static void write_block(unsigned count, BYTE value)
371 {
372 while (count != 0)
373 {
374 write_byte(value);
375 count--;
376 }
377 }
378
379 /*-----------------------------------------------------------------------------
380 This function writes a block of identical bige endian words to the CD-ROM image.
381 -----------------------------------------------------------------------------*/
382
383 static void write_word_block(unsigned count, WORD value)
384 {
385 while (count != 0)
386 {
387 write_big_endian_word(value);
388 count--;
389 }
390 }
391
392 /*-----------------------------------------------------------------------------
393 This function writes a directory record to the CD_ROM image.
394 -----------------------------------------------------------------------------*/
395
396 static void
397 write_directory_record(PDIR_RECORD d,
398 DIR_RECORD_TYPE DirType,
399 BOOL joliet)
400 {
401 unsigned identifier_size;
402 unsigned record_size;
403
404 if (joliet)
405 {
406 if (DirType == DOT_RECORD || DirType == DOT_DOT_RECORD)
407 identifier_size = 1;
408 else
409 identifier_size = strlen(d->joliet_name) * 2;
410 }
411 else
412 {
413 switch (DirType)
414 {
415 case DOT_RECORD:
416 case DOT_DOT_RECORD:
417 identifier_size = 1;
418 break;
419 case SUBDIRECTORY_RECORD:
420 /*printf ( "Subdir: %s\n", d->name_on_cd );*/
421 identifier_size = strlen(d->name_on_cd);
422 break;
423 case FILE_RECORD:
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);
428 break;
429 default:
430 identifier_size = 1;
431 break;
432 }
433 }
434 record_size = 33 + identifier_size;
435 if ((identifier_size & 1) == 0)
436 record_size++;
437 if (cd.offset + record_size > SECTOR_SIZE)
438 fill_sector();
439 write_byte((BYTE)record_size);
440 write_byte(0); // number of sectors in extended attribute record
441 if (joliet)
442 {
443 write_both_endian_dword(d->joliet_sector);
444 write_both_endian_dword(d->joliet_size);
445 }
446 else
447 {
448 write_both_endian_dword(d->sector);
449 write_both_endian_dword(d->size);
450 }
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);
463 switch (DirType)
464 {
465 case DOT_RECORD:
466 write_byte(0);
467 break;
468 case DOT_DOT_RECORD:
469 write_byte(1);
470 break;
471 case SUBDIRECTORY_RECORD:
472 if (joliet)
473 write_string_as_big_endian_unicode(d->joliet_name);
474 else
475 write_string(d->name_on_cd);
476 break;
477 case FILE_RECORD:
478 if (joliet)
479 {
480 write_string_as_big_endian_unicode(d->joliet_name);
481 }
482 else
483 {
484 write_string(d->name_on_cd);
485 if (d->extension_on_cd[0] != 0)
486 {
487 write_byte('.');
488 write_string(d->extension_on_cd);
489 }
490 write_string(";1");
491 }
492 break;
493 }
494 if ((identifier_size & 1) == 0)
495 write_byte(0);
496 }
497
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 -----------------------------------------------------------------------------*/
502
503 static void convert_date_and_time(PDATE_AND_TIME dt, time_t *time)
504 {
505 struct tm *timedef;
506
507 timedef = localtime(time);
508
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;
515 }
516
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
521 result.
522 -----------------------------------------------------------------------------*/
523
524 static int check_for_punctuation(int c, const char *name)
525 {
526 c = toupper(c & 0xFF);
527 if (!accept_punctuation_marks && !isalnum(c) && c != '_')
528 error_exit("Punctuation mark in %s", name);
529 return c;
530 }
531
532 #if defined(_WIN32) && !defined(strcasecmp)
533 #define strcasecmp stricmp
534 #endif//_WIN32
535
536 /*-----------------------------------------------------------------------------
537 This function checks to see if there's a cdname conflict.
538 -----------------------------------------------------------------------------*/
539
540 int cdname_exists ( PDIR_RECORD d )
541 {
542 PDIR_RECORD p = d->parent->first_record;
543 while ( p )
544 {
545 if ( p != d
546 && !strcasecmp ( p->name_on_cd, d->name_on_cd )
547 && !strcasecmp ( p->extension_on_cd, d->extension_on_cd ) )
548 return 1;
549 p = p->next_in_directory;
550 }
551 return 0;
552 }
553
554 void parse_filename_into_dirrecord ( const char* filename, PDIR_RECORD d, BOOL dir )
555 {
556 const char *s = filename;
557 char *t = d->name_on_cd;
558 char *n = d->name;
559 int joliet_length;
560 int filename_counter;
561 filename_counter = 1;
562 while (*s != 0)
563 {
564 if (*s == '.')
565 {
566 s++;
567 break;
568 }
569
570 if ( (size_t)(t-d->name_on_cd) < sizeof(d->name_on_cd)-1 )
571 *t++ = check_for_punctuation(*s, filename);
572 else if (!joliet)
573 error_exit ("'%s' is not ISO-9660, aborting...", filename );
574 if ( (size_t)(n-d->name) < sizeof(d->name)-1 )
575 *n++ = *s;
576 else if (!joliet)
577 error_exit ( "'%s' is not ISO-9660, aborting...", filename );
578 s++;
579 }
580 if (strlen(s) > MAX_EXTENSION_LENGTH)
581 {
582 error_exit ( "'%s' has too long extension for cdmake, aborting...", filename );
583 }
584 *t = 0;
585 strcpy(d->extension, s);
586 t = d->extension_on_cd;
587 while ( *s != 0 )
588 {
589 if ( (size_t)(t-d->extension_on_cd) < sizeof(d->extension_on_cd)-1 )
590 *t++ = check_for_punctuation(*s, filename);
591 else if (!joliet)
592 error_exit ( "'%s' is not ISO-9660, aborting...", filename );
593 s++;
594 }
595 *t = 0;
596 *n = 0;
597
598 if ( dir )
599 {
600 if (d->extension[0] != 0)
601 {
602 if (!joliet)
603 error_exit("Directory with extension %s", filename);
604 }
605 d->flags = DIRECTORY_FLAG;
606 } else
607 d->flags = 0;
608
609
610 filename_counter = 1;
611 while ( cdname_exists ( d ) )
612 {
613
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 );
617
618 if ((d->name_on_cd[8] == '.') && (strlen(d->name_on_cd) < 13))
619 error_exit ( "'%s' is a duplicate file name, aborting...", filename );
620
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);
626 filename_counter++;
627
628 }
629
630 if ( joliet )
631 {
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);
639 }
640 }
641
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 -----------------------------------------------------------------------------*/
647
648 #if _WIN32
649
650 /* Win32 version */
651 PDIR_RECORD
652 new_directory_record (struct _finddata_t *f,
653 PDIR_RECORD parent)
654 {
655 PDIR_RECORD d;
656
657 d = malloc(sizeof(DIR_RECORD));
658 if (d == NULL)
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;
663
664 /* I need the parent set before calling parse_filename_into_dirrecord(),
665 because that functions checks for duplicate file names*/
666 d->parent = parent;
667 parse_filename_into_dirrecord ( f->name, d, f->attrib & _A_SUBDIR );
668
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;
674 return d;
675 }
676
677 #else
678
679 /* Linux version */
680 PDIR_RECORD
681 new_directory_record (struct dirent *entry,
682 struct stat *stbuf,
683 PDIR_RECORD parent)
684 {
685 PDIR_RECORD d;
686 /*
687 char *s;
688 char *t;
689 char *n;
690 */
691
692 d = malloc(sizeof(DIR_RECORD));
693 if (d == NULL)
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;
698
699 /* I need the parent set before calling parse_filename_into_dirrecord(),
700 because that functions checks for duplicate file names*/
701 d->parent = parent;
702 #ifdef HAVE_D_TYPE
703 parse_filename_into_dirrecord ( entry->d_name, d, entry->d_type == DT_DIR );
704 #else
705 parse_filename_into_dirrecord ( entry->d_name, d, S_ISDIR(stbuf->st_mode) );
706 #endif
707
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;
713 return d;
714 }
715
716 #endif
717
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 -----------------------------------------------------------------------------*/
723
724 static int compare_directory_order(PDIR_RECORD p, PDIR_RECORD q)
725 {
726 int n = strcmp(p->name_on_cd, q->name_on_cd);
727 if (n == 0)
728 n = strcmp(p->extension_on_cd, q->extension_on_cd);
729 return n;
730 }
731
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 -----------------------------------------------------------------------------*/
737
738 static int compare_path_table_order(PDIR_RECORD p, PDIR_RECORD q)
739 {
740 int n = p->level - q->level;
741 if (p == q)
742 return 0;
743 if (n == 0)
744 {
745 n = compare_path_table_order(p->parent, q->parent);
746 if (n == 0)
747 n = compare_directory_order(p, q);
748 }
749 return n;
750 }
751
752 /*-----------------------------------------------------------------------------
753 This function appends the specified string to the buffer source[].
754 -----------------------------------------------------------------------------*/
755
756 static void append_string_to_source(char *s)
757 {
758 while (*s != 0)
759 *end_source++ = *s++;
760 }
761
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 -----------------------------------------------------------------------------*/
768
769 #ifdef _WIN32
770
771 static void
772 make_directory_records (PDIR_RECORD d)
773 {
774 PDIR_RECORD new_d;
775 struct _finddata_t f;
776 char *old_end_source;
777 int findhandle;
778
779 d->first_record = NULL;
780 strcpy(end_source, "*.*");
781
782 findhandle =_findfirst(source, &f);
783 if (findhandle != 0)
784 {
785 do
786 {
787 if ((f.attrib & (_A_HIDDEN | _A_SUBDIR)) == 0 && f.name[0] != '.')
788 {
789 if (strcmp(f.name, DIRECTORY_TIMESTAMP) == 0)
790 {
791 convert_date_and_time(&d->date_and_time, &f.time_write);
792 }
793 else
794 {
795 if (verbosity == VERBOSE)
796 {
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;
801 }
802 (void) new_directory_record(&f, d);
803 }
804 }
805 }
806 while (_findnext(findhandle, &f) == 0);
807
808 _findclose(findhandle);
809 }
810
811 strcpy(end_source, "*.*");
812 findhandle= _findfirst(source, &f);
813 if (findhandle)
814 {
815 do
816 {
817 if (f.attrib & _A_SUBDIR && f.name[0] != '.')
818 {
819 old_end_source = end_source;
820 append_string_to_source(f.name);
821 *end_source++ = DIR_SEPARATOR_CHAR;
822 if (verbosity == VERBOSE)
823 {
824 *end_source = 0;
825 printf("%d: directory %s\n", d->level + 1, source);
826 }
827 if (d->level < MAX_LEVEL)
828 {
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);
834 }
835 else
836 {
837 error_exit("Directory is nested too deep");
838 }
839 end_source = old_end_source;
840 }
841 }
842 while (_findnext(findhandle, &f) == 0);
843
844 _findclose(findhandle);
845 }
846
847 // sort directory
848 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
849 }
850
851 #else
852
853 /* Linux version */
854 static void
855 make_directory_records (PDIR_RECORD d)
856 {
857 PDIR_RECORD new_d;
858 DIR *dirp;
859 struct dirent *entry;
860 char *old_end_source;
861 struct stat stbuf;
862 char buf[MAX_PATH];
863
864 d->first_record = NULL;
865
866 #ifdef HAVE_D_TYPE
867 dirp = opendir(source);
868 if (dirp != NULL)
869 {
870 while ((entry = readdir (dirp)) != NULL)
871 {
872 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
873 continue; // skip self and parent
874
875 if (entry->d_type == DT_REG) // normal file
876 {
877 // Check for an absolute path
878 if (source[0] == DIR_SEPARATOR_CHAR)
879 {
880 strcpy(buf, source);
881 strcat(buf, DIR_SEPARATOR_STRING);
882 strcat(buf, entry->d_name);
883 }
884 else
885 {
886 if (!getcwd(buf, sizeof(buf)))
887 error_exit("Can't get CWD: %s\n", strerror(errno));
888 strcat(buf, DIR_SEPARATOR_STRING);
889 strcat(buf, source);
890 strcat(buf, entry->d_name);
891 }
892
893 if (stat(buf, &stbuf) == -1)
894 {
895 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
896 return;
897 }
898
899 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
900 {
901 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
902 }
903 else
904 {
905 if (verbosity == VERBOSE)
906 {
907 printf("%d: file %s\n", d->level, buf);
908 }
909 (void) new_directory_record(entry, &stbuf, d);
910 }
911 }
912 }
913 closedir(dirp);
914 }
915 else
916 {
917 error_exit("Can't open %s\n", source);
918 return;
919 }
920
921 dirp = opendir(source);
922 if (dirp != NULL)
923 {
924 while ((entry = readdir (dirp)) != NULL)
925 {
926 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
927 continue; // skip self and parent
928
929 if (entry->d_type == DT_DIR) // directory
930 {
931 old_end_source = end_source;
932 append_string_to_source(entry->d_name);
933 *end_source++ = DIR_SEPARATOR_CHAR;
934 *end_source = 0;
935 if (verbosity == VERBOSE)
936 {
937 printf("%d: directory %s\n", d->level + 1, source);
938 }
939 if (d->level < MAX_LEVEL)
940 {
941 // Check for an absolute path
942 if (source[0] == DIR_SEPARATOR_CHAR)
943 {
944 strcpy(buf, source);
945 }
946 else
947 {
948 if (!getcwd(buf, sizeof(buf)))
949 error_exit("Can't get CWD: %s\n", strerror(errno));
950 strcat(buf, DIR_SEPARATOR_STRING);
951 strcat(buf, source);
952 }
953
954 if (stat(buf, &stbuf) == -1)
955 {
956 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
957 return;
958 }
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);
964 }
965 else
966 {
967 error_exit("Directory is nested too deep");
968 }
969 end_source = old_end_source;
970 *end_source = 0;
971 }
972 }
973 closedir(dirp);
974 }
975 else
976 {
977 error_exit("Can't open %s\n", source);
978 return;
979 }
980
981 #else
982
983 dirp = opendir(source);
984 if (dirp != NULL)
985 {
986 while ((entry = readdir (dirp)) != NULL)
987 {
988 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
989 continue; // skip self and parent
990
991 // Check for an absolute path
992 if (source[0] == DIR_SEPARATOR_CHAR)
993 {
994 strcpy(buf, source);
995 strcat(buf, DIR_SEPARATOR_STRING);
996 strcat(buf, entry->d_name);
997 }
998 else
999 {
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);
1005 }
1006
1007 if (stat(buf, &stbuf) == -1)
1008 {
1009 error_exit("Can't access '%s' (%s)\n", buf, strerror(errno));
1010 return;
1011 }
1012
1013 if (S_ISDIR(stbuf.st_mode))
1014 {
1015 old_end_source = end_source;
1016 append_string_to_source(entry->d_name);
1017 *end_source++ = DIR_SEPARATOR_CHAR;
1018 *end_source = 0;
1019 if (verbosity == VERBOSE)
1020 {
1021 printf("%d: directory %s\n", d->level + 1, source);
1022 }
1023
1024 if (d->level < MAX_LEVEL)
1025 {
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);
1031 }
1032 else
1033 {
1034 error_exit("Directory is nested too deep");
1035 }
1036
1037 end_source = old_end_source;
1038 *end_source = 0;
1039 }
1040 else if (S_ISREG(stbuf.st_mode))
1041 {
1042 if (strcmp(entry->d_name, DIRECTORY_TIMESTAMP) == 0)
1043 {
1044 convert_date_and_time(&d->date_and_time, &stbuf.st_ctime);
1045 }
1046 else
1047 {
1048 if (verbosity == VERBOSE)
1049 {
1050 printf("%d: file %s\n", d->level, buf);
1051 }
1052 (void) new_directory_record(entry, &stbuf, d);
1053 }
1054 }
1055 }
1056 closedir (dirp);
1057 }
1058 else
1059 {
1060 error_exit("Can't open %s\n", source);
1061 return;
1062 }
1063
1064 #endif
1065
1066 // sort directory
1067 d->first_record = sort_linked_list(d->first_record, 0, compare_directory_order);
1068 }
1069
1070 #endif
1071
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
1075 is recursive.
1076 -----------------------------------------------------------------------------*/
1077
1078 static void get_file_specifications(PDIR_RECORD d)
1079 {
1080 if (d != &root)
1081 {
1082 get_file_specifications(d->parent);
1083 if (d->joliet_name == NULL)
1084 append_string_to_source(d->name);
1085 else
1086 append_string_to_source(d->joliet_name);
1087
1088 if (((d->flags & DIRECTORY_FLAG) == 0 || joliet) && d->extension[0] != 0)
1089 {
1090 if (d->joliet_name == NULL)
1091 {
1092 *end_source++ = '.';
1093 append_string_to_source(d->extension);
1094 }
1095 }
1096 if (d->flags & DIRECTORY_FLAG)
1097 *end_source++ = DIR_SEPARATOR_CHAR;
1098 }
1099 }
1100
1101 static void pass(void)
1102 {
1103 PDIR_RECORD d;
1104 PDIR_RECORD q;
1105 unsigned int i;
1106 char *t;
1107 unsigned int index;
1108 unsigned int name_length;
1109 DWORD size;
1110 DWORD number_of_sectors;
1111 char *old_end_source;
1112 int n;
1113 FILE *file;
1114
1115 // first 16 sectors are zeros
1116
1117 write_block(16 * SECTOR_SIZE, 0);
1118
1119 // Primary Volume Descriptor
1120
1121 write_string("\1CD001\1");
1122 write_byte(0);
1123 write_block(32, ' '); // system identifier
1124
1125 t = volume_label;
1126 for (i = 0; i < 32; i++)
1127 write_byte( (BYTE)( (*t != 0) ? toupper(*t++) : ' ' ) );
1128
1129 write_block(8, 0);
1130 write_both_endian_dword(total_sectors);
1131 write_block(32, 0);
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
1149 write_byte(0);
1150 write_string("0000000000000000"); // most recent modification
1151 write_byte(0);
1152 write_string("0000000000000000"); // volume expires
1153 write_byte(0);
1154 write_string("0000000000000000"); // volume is effective
1155 write_byte(0);
1156 write_byte(1);
1157 write_byte(0);
1158 fill_sector();
1159
1160
1161 // Boot Volume Descriptor
1162
1163 if (eltorito == TRUE)
1164 {
1165 write_byte(0);
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
1171 fill_sector();
1172 }
1173
1174 // Supplementary Volume Descriptor
1175
1176 if (joliet)
1177 {
1178 write_string("\2CD001\1");
1179 write_byte(0);
1180
1181 write_word_block(16, L' '); // system identifier
1182
1183 t = volume_label;
1184 for (i = 0; i < 16; i++)
1185 write_big_endian_word( (BYTE)( (*t != 0) ? *t++ : ' ' ) );
1186
1187 write_block(8, 0);
1188 write_both_endian_dword(total_sectors);
1189 write_string("%/E");
1190 write_block(29, 0);
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
1208 write_byte(0);
1209 write_string("0000000000000000"); // most recent modification
1210 write_byte(0);
1211 write_string("0000000000000000"); // volume expires
1212 write_byte(0);
1213 write_string("0000000000000000"); // volume is effective
1214 write_byte(0);
1215 write_byte(1);
1216 write_byte(0);
1217 fill_sector();
1218 }
1219
1220
1221 // Volume Descriptor Set Terminator
1222 write_string("\377CD001\1");
1223 fill_sector();
1224
1225 // Boot Catalog
1226 if (eltorito == TRUE)
1227 {
1228 boot_catalog_sector = cd.sector;
1229
1230 // Validation entry
1231 write_byte(1);
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
1238
1239 // default entry
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
1247
1248 fill_sector();
1249 }
1250
1251 // Boot Image
1252 if (eltorito == TRUE)
1253 {
1254 boot_image_sector = cd.sector;
1255
1256 file = fopen(bootimage, "rb");
1257 if (file == NULL)
1258 error_exit("Can't open %s\n", bootimage);
1259 fseek(file, 0, SEEK_END);
1260 size = ftell(file);
1261 fseek(file, 0, SEEK_SET);
1262 if (size == 0 || (size % 2048))
1263 {
1264 fclose(file);
1265 error_exit("Invalid boot image size (%lu bytes)\n", size);
1266 }
1267 boot_image_size = size / 512;
1268 while (size > 0)
1269 {
1270 n = BUFFER_SIZE - cd.count;
1271 if ((DWORD) n > size)
1272 n = size;
1273 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
1274 {
1275 fclose(file);
1276 error_exit("Read error in file %s\n", bootimage);
1277 }
1278 cd.count += n;
1279 if (cd.count == BUFFER_SIZE)
1280 flush_buffer();
1281 cd.sector += n / SECTOR_SIZE;
1282 cd.offset += n % SECTOR_SIZE;
1283 size -= n;
1284 }
1285 fclose(file);
1286 // fill_sector();
1287 }
1288
1289 // Little Endian Path Table
1290 little_endian_path_table_sector = cd.sector;
1291 write_byte(1);
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);
1295 write_byte(0);
1296 write_byte(0);
1297
1298 index = 1;
1299 root.path_table_index = 1;
1300 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1301 {
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)
1309 write_byte(0);
1310 d->path_table_index = ++index;
1311 }
1312
1313 path_table_size = (cd.sector - little_endian_path_table_sector) *
1314 SECTOR_SIZE + cd.offset;
1315 fill_sector();
1316
1317 // Big Endian Path Table
1318
1319 big_endian_path_table_sector = cd.sector;
1320 write_byte(1);
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);
1324 write_byte(0);
1325 write_byte(0);
1326
1327 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1328 {
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)
1336 write_byte(0);
1337 }
1338 fill_sector();
1339
1340 if (joliet)
1341 {
1342 // Little Endian Path Table
1343
1344 joliet_little_endian_path_table_sector = cd.sector;
1345 write_byte(1);
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);
1349 write_byte(0);
1350 write_byte(0);
1351
1352 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1353 {
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);
1360 }
1361
1362 joliet_path_table_size = (cd.sector - joliet_little_endian_path_table_sector) *
1363 SECTOR_SIZE + cd.offset;
1364 fill_sector();
1365
1366 // Big Endian Path Table
1367
1368 joliet_big_endian_path_table_sector = cd.sector;
1369 write_byte(1);
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);
1373 write_byte(0);
1374 write_byte(0);
1375
1376 for (d = root.next_in_path_table; d != NULL; d = d->next_in_path_table)
1377 {
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);
1384 }
1385 fill_sector();
1386 }
1387
1388 // directories and files
1389 for (d = &root; d != NULL; d = d->next_in_path_table)
1390 {
1391 // write directory
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)
1396 {
1397 write_directory_record(q,
1398 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1399 FALSE);
1400 }
1401 fill_sector();
1402 d->size = (cd.sector - d->sector) * SECTOR_SIZE;
1403
1404 // write directory for joliet
1405 if (joliet)
1406 {
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)
1411 {
1412 write_directory_record(q,
1413 q->flags & DIRECTORY_FLAG ? SUBDIRECTORY_RECORD : FILE_RECORD,
1414 TRUE);
1415 }
1416 fill_sector();
1417 d->joliet_size = (cd.sector - d->joliet_sector) * SECTOR_SIZE;
1418 bytes_in_directories += d->joliet_size;
1419 }
1420
1421 number_of_directories++;
1422 bytes_in_directories += d->size;
1423
1424 // write file data
1425 for (q = d->first_record; q != NULL; q = q->next_in_directory)
1426 {
1427 if ((q->flags & DIRECTORY_FLAG) == 0)
1428 {
1429 q->sector = q->joliet_sector = cd.sector;
1430 size = q->size;
1431 if (cd.file == NULL)
1432 {
1433 number_of_sectors = (size + SECTOR_SIZE - 1) / SECTOR_SIZE;
1434 cd.sector += number_of_sectors;
1435 number_of_files++;
1436 bytes_in_files += size;
1437 unused_bytes_at_ends_of_files +=
1438 number_of_sectors * SECTOR_SIZE - size;
1439 }
1440 else
1441 {
1442 old_end_source = end_source;
1443 get_file_specifications(q);
1444 *end_source = 0;
1445 if (verbosity == VERBOSE)
1446 printf("Writing %s\n", source);
1447 file = fopen(source, "rb");
1448 if (file == NULL)
1449 error_exit("Can't open %s\n", source);
1450 fseek(file, 0, SEEK_SET);
1451 while (size > 0)
1452 {
1453 n = BUFFER_SIZE - cd.count;
1454 if ((DWORD) n > size)
1455 n = size;
1456 if (fread (cd.buffer + cd.count, n, 1, file) < 1)
1457 {
1458 fclose(file);
1459 error_exit("Read error in file %s\n", source);
1460 }
1461 cd.count += n;
1462 if (cd.count == BUFFER_SIZE)
1463 flush_buffer();
1464 cd.sector += n / SECTOR_SIZE;
1465 cd.offset += n % SECTOR_SIZE;
1466 size -= n;
1467 }
1468 fclose(file);
1469 end_source = old_end_source;
1470 fill_sector();
1471 }
1472 }
1473 }
1474 }
1475
1476 total_sectors = (DWORD)cd.sector;
1477 }
1478
1479 static char HELP[] =
1480 "CDMAKE [-q] [-v] [-p] [-s N] [-m] [-b bootimage] [-j] source volume image\n"
1481 "\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";
1496
1497 /*-----------------------------------------------------------------------------
1498 Program execution starts here.
1499 -----------------------------------------------------------------------------*/
1500
1501 int main(int argc, char **argv)
1502 {
1503 BOOL q_option = FALSE;
1504 BOOL v_option = FALSE;
1505 int i;
1506 char *t;
1507
1508 if (argc < 2)
1509 {
1510 puts(HELP);
1511 return 1;
1512 }
1513
1514 if (setjmp(error))
1515 return 1;
1516
1517 // initialize root directory
1518
1519 cd.buffer = malloc (BUFFER_SIZE);
1520 if (cd.buffer == NULL)
1521 error_exit("Insufficient memory");
1522
1523 memset(&root, 0, sizeof(root));
1524 root.level = 1;
1525 root.flags = DIRECTORY_FLAG;
1526
1527 // initialize CD-ROM write buffer
1528
1529 cd.file = NULL;
1530 cd.filespecs[0] = 0;
1531
1532 // initialize parameters
1533
1534 verbosity = NORMAL;
1535 size_limit = 0;
1536 show_progress = FALSE;
1537 accept_punctuation_marks = FALSE;
1538 source[0] = 0;
1539 volume_label[0] = 0;
1540
1541 eltorito = FALSE;
1542
1543 // scan command line arguments
1544
1545 for (i = 1; i < argc; i++)
1546 {
1547 if (memcmp(argv[i], "-s", 2) == 0)
1548 {
1549 t = argv[i] + 2;
1550 if (*t == 0)
1551 {
1552 if (++i < argc)
1553 t = argv[i];
1554 else
1555 error_exit("Missing size limit parameter");
1556 }
1557 while (isdigit(*t))
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
1562 }
1563 else if (strcmp(argv[i], "-q") == 0)
1564 q_option = TRUE;
1565 else if (strcmp(argv[i], "-v") == 0)
1566 v_option = TRUE;
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)
1572 joliet = TRUE;
1573 else if (strcmp(argv[i], "-b") == 0)
1574 {
1575 strcpy(bootimage, argv[++i]);
1576 eltorito = TRUE;
1577 }
1578 else if (i + 2 < argc)
1579 {
1580 strcpy(source, argv[i++]);
1581 strncpy(volume_label, argv[i++], sizeof(volume_label) - 1);
1582 strcpy(cd.filespecs, argv[i]);
1583 }
1584 else
1585 error_exit("Missing command line argument");
1586 }
1587 if (v_option)
1588 {
1589 show_progress = FALSE;
1590 verbosity = VERBOSE;
1591 }
1592 else if (q_option)
1593 {
1594 verbosity = QUIET;
1595 show_progress = FALSE;
1596 }
1597 if (source[0] == 0)
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");
1603
1604
1605 // set source[] and end_source to source directory, with a terminating directory separator
1606
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;
1612
1613 // scan all files and create directory structure in memory
1614
1615 make_directory_records(&root);
1616
1617 // sort path table entries
1618
1619 root.next_in_path_table = sort_linked_list(root.next_in_path_table, 1,
1620 compare_path_table_order);
1621
1622 // initialize CD-ROM write buffer
1623
1624 cd.file = NULL;
1625 cd.sector = 0;
1626 cd.offset = 0;
1627 cd.count = 0;
1628
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
1631
1632 number_of_files = bytes_in_files = number_of_directories =
1633 bytes_in_directories = unused_bytes_at_ends_of_files =0;
1634 pass();
1635
1636 if (verbosity >= NORMAL)
1637 {
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("=============");
1650 }
1651
1652 if (size_limit != 0 && total_sectors > size_limit)
1653 error_exit("Size limit exceeded");
1654
1655 // re-initialize CD-ROM write buffer
1656
1657 cd.file = fopen(cd.filespecs, "w+b");
1658 if (cd.file == NULL)
1659 error_exit("Can't open image file %s", cd.filespecs);
1660 cd.sector = 0;
1661 cd.offset = 0;
1662 cd.count = 0;
1663
1664
1665 // make writing pass over directory structure
1666
1667 pass();
1668
1669 if (cd.count > 0)
1670 flush_buffer();
1671 if (show_progress)
1672 printf("\r \n");
1673 if (fclose(cd.file) != 0)
1674 {
1675 cd.file = NULL;
1676 error_exit("File write error in image file %s", cd.filespecs);
1677 }
1678
1679 if (verbosity >= NORMAL)
1680 puts("CD-ROM image made successfully");
1681
1682 release_memory();
1683 return 0;
1684 }
1685
1686
1687 /* EOF */