7

I wanted to write a simple hello world program in DOSBox, so I downloaded Bruce's C Compiler from here. And wrote this program (using edit.exe):

#include <stdio.h>
int main() {
    printf("Hello, World!\n");
    return 0;
}

I ran bcc like this: bcc -o hello hello.c, but I get this error every time:

C:\BCC\DEVEL\BCC\BIN\LD86.EXE: HELLO.C has bad magic number

I tried bcc hello.c -o hello.exe and got the same error.

I don't know why this is happening, if anyone can tell me please do.

I'm running DOSBox 0.74 on 64-bit Windows 10.

EDIT: I ran bcc with filenames in all uppercase (as what you would get from autocomplete), but if you type it out in lowercase the compiler seems to work just fine.

  • Weird, it works for me... What version of DOSBox are you using? On what platform? (BTW, you should run bcc -o hello.exe hello.c instead, if you want to be able to run the program without renaming it first.) – Stephen Kitt Nov 2 '18 at 14:47
  • 2
    HELLO.C shouldn't have a magic number at all. Did you create it in an editor that saves in Unicode format, or adds text formatting, or something similar, instead of writing plain ASCII text? – alephzero Nov 2 '18 at 15:16
  • What happens if you try bcc /o hello hello.c? – DrSheldon Nov 2 '18 at 16:33
  • @DrSheldon BCC only uses Unix-style short flags. – MarinB Nov 2 '18 at 16:37
  • @DrSheldon “LD86.EXE: cannot open input file /o” ;-) – Stephen Kitt Nov 2 '18 at 16:37
10

This has to do with the way bcc handles file extensions. First, a note: code in this answer may be slightly modified from the original (for simplicity, readability and to aid understanding), but the basic idea is preserved. Having said that, let's begin the analysis.


In bcc.c, the file names on the command line (i.e., all those arguments not starting with -) are parsed in getargs, called from main:

for(ar=1; ar<argc; )
    if (argv[ar][0] != '-') {
        append_file(argv[ar++], 0);
        file_count++;
    } else ...

You can see that append_file is being called with 0 as the second argument for each of the files. Following the code down to there (still in bcc.c), we see what happens when this is the case:

void append_file (char *filename, int ftype) {
   struct file_list *newfile = xalloc(sizeof(struct file_list));
   char *s, *name;

   newfile->file = copystr(filename); name = copystr(filename);

   char *s = strrchr(name, '.');
   if (ftype) {
      newfile->name = copystr(name); newfile->filetype = ftype;
   } else if (s && s == name + strlen(name) - 2) {
      newfile->filetype = s[1]; *s = 0; newfile->name = copystr(name);
   } else ...

Since ftype is zero, it skips the first if block and enters the else if block (it finds the .<extension> exactly two characters before the string terminator, meaning <extension> is a single character).

So it simply stores the character as-is, in all its case-sensitive glory:

  newfile->filetype = s[1];

If you then look back in the main function, you'll find it acts on the file extension, performing specific actions:

for (next_file = <all-files>) {
    if (next_file->filetype == 'o') continue;

    /* Assembler that's not to be optimised. */
    if (do_preproc && next_file->filetype == 'x') run_aspreproc(next_file);
    if (do_preproc && next_file->filetype == 'S') run_aspreproc(next_file);
    if (do_as      && next_file->filetype == 's') run_as(next_file);

    /* C source */
    if (do_preproc && next_file->filetype == 'c') run_preproc(next_file);
    if (do_unproto && next_file->filetype == 'i') run_unproto(next_file);
    if (do_compile && next_file->filetype == 'i') run_compile(next_file);
    if (do_optim   && next_file->filetype == 's') run_optim(next_file);
    if (do_as      && next_file->filetype == 's') run_as(next_file);
}

if (do_link && !error_count)
    run_link();

Note that first line following the "C source" comment, it checks for lower-case c only.

Each run_<phase> function is responsible for removing the original file from the list and adding a newly generated file. For example, x.c may be turned into x.i, the original file removed from the list and the generated file added.

However, run_preproc is only being called for any file with a lower-case c extension. Indeed, no run_<phase> function gets called for the upper-case variant of C, meaning that file is left in the file list for the linker.


So, in short:

  • the extension is identified as upper-case C;
  • this is not considered a C file (or any type of file the compiler can handle) by the file type detector in main;
  • it is therefore be left in the file list to be passed to the linker for resolution.

Then, of course, the linker has no idea what to do with a C source file so it rightly complains bitterly - the magic it's looking for is almost certainly the header at the start of an object or library file, neither of which the C source file contains.

And, in fact, this is further supported by the fact you can use an upper-case source file, just not for the extension bit:

C:\SRC> BCC -O HELLO.EXE HELLO.C
C:\BCCBIN\LD86.EXE: HELLO.C has bad magic number

C:\SRC> BCC -O HELLO.EXE HELLO.c
C:\SRC> hello
Hello, World!

One way to fix this would be to just treat upper-case C as a valid extension for C source code as well, something like:

if (do_preproc && tolower(next_file->filetype) == 'c') run_preproc(next_file);
  • Thank you for the analysis, I figured out that I need to pass lowercase filenames, but didn't understand why. – MarinB Nov 16 '18 at 11:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.