0

I am trying to make a C program about a Nuclear Missile launch. Below is my source code so far:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int nukeLaunchCode = 618243;
int NLC_input;
char *address;

int main(void) {
    address = malloc(sizeof(char) * 60);
    printf("\033[0;34m[INFO] C.E.R.B.E.R.U.S Activating");
    fflush(stdout);
    for (int i = 0; i < 3; i++){
        printf(".");
        sleep(1);
    }
    fflush(stdout);
    printf("\n");
    printf("\033[0;32m[INFO] C.E.R.B.E.R.U.S Initialized. Enter destination for nuke in address form: ");
    fgets(address, 60, stdin);
    printf("\033[0;32m[INFO] Geo locating address via Advanced UAV");
    fflush(stdout);
    for (int i = 0; i < 6; i++) {
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    fflush(stdout);
    printf("\033[0;31m[WARNING] Target Acquired. Enter 6-digit Nuke Confirmation code to proceed: ");
    fgets(&NLC_input, 7, stdin);
    return 0;

}

The problem is the fgets at the end of the code. I get a warning saying:

Incompatible pointer types passing 'int *' to parameter of type 'char *'

I've tried using scanf wiht the "%d" conversion symbol and then I get another warning saying:

'scanf' used to convert a string to an integer value, but function will not 
report conversion errors; consider using 'strtol' instead

Can someone please tell me what I'm doing wrong? Also, I've included the fflush functions because i was trying to do something cool. I wanted to print a certain amount of dots every second after a sentence was printed to animate a loading kind of thing. For some reason, without the fflush functions, commands are executed out of order and the dots don't print. But even with the fflush functions, the first line that's printed doesn't print the 3 dots until the next line is printed. I'm pretty sure this is a misplacement in the fflush functions, but can someone help me with that too please? Thank you!

Matthew Schell
  • 599
  • 1
  • 6
  • 19
  • 1
    `int NLC_input;` so `&NLC_input` is what? (hint: *Pointer to* `int`) The prototype of `fgets()` is `char *fgets(char *s, int size, FILE *stream);` where the 1st argument is `char*` not `int*`. Just use `char tmp[1024]` and `fgets (tmp, sizeof tmp, stdin)` and then `if (sscanf (tmp, "%d", &NLC_input) != 1) { /* error invalid integer input */ }` – David C. Rankin Dec 02 '20 at 02:25
  • The warning to use `strtol` instead is a new one ... But, personally, I've never been a fan of `scanf` [primarily for the stated reason]. I've always used `fgets` followed by `strtol`. For some code that illustrates what I mean, see my recent answer: https://stackoverflow.com/questions/65011769/check-if-all-values-entered-into-char-array-are-numerical/65013419#65013419 It has some precanned routines for prompting a user for a string or a number, which is what I think you're looking for. – Craig Estey Dec 02 '20 at 02:45
  • Please read [how to ask a question](https://stackoverflow.com/help/how-to-ask). You should focus on one little problem at a time when asking questions, then you should reduce your code to provide a [minimal working example](https://stackoverflow.com/help/minimal-reproducible-example). There's not a huge amount of code here, but it's very far from minimal. This should really be a one-line problem (and when reduced, it's actually probably two or three little problems that should be separate questions, or - more likely - already have good answers). – Elliott Dec 02 '20 at 03:19

1 Answers1

4

Alright, let's do something cool and get you straightened out. To begin with, the proper prototype for fgets() is:

char *fgets(char *s, int size, FILE *stream);

man 3 fgets

In your code you have declared int NLC_input;, but then attempt to use the address of NLC_input as the first parameter to fgets(). Not allowed. Taking the address of a int result in a pointer to int which is int* and does not match the pointer to char (e.g. char*) that fgets() requires.

Further, you cannot convert the user-input of characters to a numeric form (like int) with fgets(). You must use either strtol() or sscanf() to convert the digits contained in the array to a number. (but Good Job using fgets() for user-input, far better than trying to read directly with scanf())

Let's do a quick rundown of your code with additions:

#include <stdio.h>
#include <unistd.h>

#define MAXC 1024       /* if you need a constant, #define on (or more) */
#define ADRSZ  60

(stdlib.h removed as there is no need to dynamically allocate address)

int main(void) {
    
    char address[ADRSZ], tmp[MAXC];             /* avoid global variables */
    int nukeLaunchCode = 618243, NLC_input;

(note: if you don't plan to change nukeLaunchCode, then #define nukeLaunchCode 618243 would be fine -- but note, macros are generally defined in ALL-CAPS in C. Another alternative would be const int nukeLaunchCode 618243;)

You only need fflush() when you are ready for output, you include it is few additional times where it will make no difference. Instead:

    printf ("\033[0;34m[INFO] C.E.R.B.E.R.U.S Activating");
    for (int i = 0; i < 3; i++) {
        putchar ('.');              /* don't use printf for single-char */
        fflush(stdout);             /* fflush only needed when output required */
        sleep(1);
    }
    printf ("\n\033[0;32m[INFO] C.E.R.B.E.R.U.S Initialized. "
            "Enter destination for nuke in address form: ");

(note: the '\n' has been included at the beginning of the second printf())

Now on to input:

    if (!fgets(address, 60, stdin)) {           /* validate EVERY user input */
        puts ("(user canceled input)");
        return 0;
    }
    printf ("\033[0;32m[INFO] Geo locating address via Advanced UAV");
    for (int i = 0; i < 6; i++) {
        putchar ('.');
        fflush(stdout);
        sleep(1);
    }
    printf ("\n\033[0;31m[WARNING] Target Acquired. "
            "Enter 6-digit Nuke Confirmation code to proceed: ");

(note: a user can cancel input by generating a manual EOF by pressing Ctrl + d, (or Ctrl + z on windows) and that is not and error, so you should handle the EOF case for the user)

Just read the input that you will convert to a value in NLC_input into a temporary array using fgets(), e.g.

    if (!fgets (tmp, sizeof tmp, stdin)) {      /* fill temporary array */
        puts ("(user canceled input)");
        return 0;
    }
    if (sscanf (tmp, "%d", &NLC_input) != 1) {  /* validate EVERY conversion */
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }

Now on to validating the launch code:

    if (NLC_input != nukeLaunchCode) {          /* validate launch code */
        fputs ("error: Invalid Code, self-destruct sequence started ", stdout);
        for (int i = 0; i < 6; i++) {
            putchar ('.');
            fflush(stdout);
            sleep(1);
        }
        puts (" B O O M ! ! !");
        return 1;
    }
    
    puts ("Launch Code Valid, continuing ..."); 
}

Example Use/Output

Give it a go:

$ ./bin/launchcode
[INFO] C.E.R.B.E.R.U.S Activating...
[INFO] C.E.R.B.E.R.U.S Initialized. Enter destination for nuke in address form: N34W67.123
[INFO] Geo locating address via Advanced UAV......
[WARNING] Target Acquired. Enter 6-digit Nuke Confirmation code to proceed: 618244
error: Invalid Code, self-destruct sequence started ...... B O O M ! ! !

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Wow thank you! But I have a warning. I know warnings might not be too fatal but I always like to fix and optimize as much as possible because I think it's good practice. In the sscanf function, I'm getting told to use strtol again. Can you please explain why? – Matthew Schell Dec 02 '20 at 03:08
  • Always enable full warnings and do not accept code until it compiles without warning. `strtol()` verses `sscanf()` can be used in many of the same circumstances. Ideally, `strtol()` is used when you do not know the number of integers contained in the string and need to work your way down the input string converting values as you go. That said, `strtol()` provide far superior error detection and reporting than `sscanf()`. `sscanf()` on the other hand is used when you do know how many inputs and that they will not overflow. It is use is convenient and provides a success/failure of conversion. – David C. Rankin Dec 02 '20 at 03:13
  • The prototype for `strtol()` is `long int strtol(const char *nptr, char **endptr, int base);` The end-pointer parameter `endptr` can be `NULL`. The number-pointer `nptr` must point to a character string containing the digits to be converted. The `base` is the number base, e.g. `2`, `8`, `10`, `16`, etc.. Use of `0` for the base will use the base suggested by either octal, decimal or hex formatted string `03` (octal `3`) or `0xff` (hex `ff`) Using `endptr` (note it is a *pointer to pointer to* `char`), you must compare `nptr != endptr` to validate digits converted, and that `errno` is not set. – David C. Rankin Dec 02 '20 at 03:18
  • You can edit your question and ADD your attempt with `strtol()` as an addition below your original question and I can help further. Or see [Detect strtol Failure](https://stackoverflow.com/a/26083517/3422102) – David C. Rankin Dec 02 '20 at 03:21
  • I'll try the strtol function tomorrow, it's getting a little late for me. But thanks! – Matthew Schell Dec 02 '20 at 03:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225386/discussion-between-matthew-schell-and-david-c-rankin). – Matthew Schell Dec 02 '20 at 03:30