[–] 1 point 4 months ago

I would do the loop instead the if condition, where letter != 'Y' or 'N' generally speaking, using while(1) for x86_64 programming is bad practice, specific cases like embedded system design is where it's more commonly used.

  • source
  • [–] 2 points 4 months ago

    I'd recommend not to alias compilers like that. Rather, if you're interested, you could just use a simple Makefile like shown below and get it over with. Save it into a file called Makefile (yes, uppercase M followed by akefile all lowercase) in the same directory as your program.c file.

    Note, I have used -std=c89 but you can delete that line or maybe even use a more modern standard such as -std=c23 if you like. If you delete, both gcc and clang will default to the latest ISO standard supported by that version of compiler.

    Also since you're compiling only a single file, I have used program.c as input producing program executable on UNIX. To run this Makefile you need to invoke make from the same directory as Makefile on command-line as:

    make W=1
    

    to enable all the warnings and compile. If you set W=0 it will not warn or even print any diagnostic unless compiler really cannot generate the executable. You can also pass D=0 for optimised/release build, D=1 for debug build so you can backtrack in gdb. You can also pass A=1 if you want address sanitisers enabled. It will report if your program is touching or even looking at pointers/memory regions the wrong way, leaking memory or just generally doing operations that cannot guarantee correct behaviour. Passing V=1 will display the exact command executed to compile, otherwise it will silently compile.

    Here's the full Makefile I use:

    # Control build verbosity.
    V                               ?= 0
    ifeq ($(V),1)
        Q                           :=
    else
        Q                           := @
    endif
    # Control DEBUG (1) or RELEASE (0) build.
    D                               ?= 1
    # Add some warning flags?
    W                               ?= 0
    # Add some address/undefined sanitiser flags?
    A                               ?= 1
    
    ifeq ($(shell $(CC) -v 2>&1 | grep -c "gcc version"),1)
        COMPILER                    := gnu
    else ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"),1)
        COMPILER                    := llvm
    endif
    
    # Delete environment garbage so we can generate absolutely controlled 
    LDFLAGS                         :=
    CFLAGS                          :=
    
    ifeq ($(D),1)
        CFLAGS_DEFS                 += -D_DEBUG
    else
        CFLAGS_DEFS                 += -DNDEBUG
    endif
    CFLAGS_DEFS                     += -D_XOPEN_SOURCE=600
    ifeq ($(D),0)
        CFLAGS_DEFS                 += -D_FORTIFY_SOURCE=2
    endif
    CFLAGS_DEFS                     += -D_LARGEFILE64_SOURCE=1
    CFLAGS_DEFS                     += -D_LARGEFILE_SOURCE=1
    
    ifeq ($(W),1)
        CFLAGS_STDS                 += -std=c89
        CFLAGS_STDS                 += -Wpedantic
        CFLAGS_STDS                 += -pedantic-errors
    
        CFLAGS_STDS                 += -Werror
        CFLAGS_STDS                 += -Wall
        CFLAGS_STDS                 += -Wextra
        ifeq ($(COMPILER),gnu)
            CFLAGS_STDS             += -Wbad-function-cast
            CFLAGS_STDS             += -Wcast-align
            CFLAGS_STDS             += -Wcast-qual
            CFLAGS_STDS             += -Wduplicated-branches
            CFLAGS_STDS             += -Wduplicated-cond
            CFLAGS_STDS             += -Wfloat-equal
            CFLAGS_STDS             += -Wformat-nonliteral
            CFLAGS_STDS             += -Wformat-security
            CFLAGS_STDS             += -Wformat-signedness
            CFLAGS_STDS             += -Wformat-truncation=2
            CFLAGS_STDS             += -Wformat=2
            CFLAGS_STDS             += -Winline
            CFLAGS_STDS             += -Wlogical-op
            CFLAGS_STDS             += -Wmissing-declarations
            CFLAGS_STDS             += -Wmissing-format-attribute
            CFLAGS_STDS             += -Wmissing-include-dirs
            CFLAGS_STDS             += -Wmissing-noreturn
            CFLAGS_STDS             += -Wmissing-prototypes
            CFLAGS_STDS             += -Wnested-externs
            CFLAGS_STDS             += -Wno-unused-result
            CFLAGS_STDS             += -Wnull-dereference
            CFLAGS_STDS             += -Wold-style-definition
            CFLAGS_STDS             += -Wpointer-arith
            CFLAGS_STDS             += -Wpointer-sign
            CFLAGS_STDS             += -Wredundant-decls
            CFLAGS_STDS             += -Wrestrict
            CFLAGS_STDS             += -Wreturn-type
            CFLAGS_STDS             += -Wshadow
            CFLAGS_STDS             += -Wsign-compare
            CFLAGS_STDS             += -Wsign-conversion
            CFLAGS_STDS             += -Wstrict-aliasing
            CFLAGS_STDS             += -Wstrict-prototypes
            CFLAGS_STDS             += -Wswitch-enum
            CFLAGS_STDS             += -Wtrampolines
            CFLAGS_STDS             += -Wundef
            CFLAGS_STDS             += -Wuninitialized
            CFLAGS_STDS             += -Wunreachable-code
            CFLAGS_STDS             += -Wunused
            CFLAGS_STDS             += -Wunused-but-set-parameter
            CFLAGS_STDS             += -Wunused-but-set-variable
            CFLAGS_STDS             += -Wunused-label
            CFLAGS_STDS             += -Wunused-local-typedefs
            CFLAGS_STDS             += -Wunused-parameter
            CFLAGS_STDS             += -Wunused-variable
            CFLAGS_STDS             += -Wwrite-strings
        else
            CFLAGS_STDS             += -Wmost
            CFLAGS_STDS             += -Warray-bounds-pointer-arithmetic
            CFLAGS_STDS             += -Wassign-enum
            CFLAGS_STDS             += -Wcomma
            CFLAGS_STDS             += -Wconditional-uninitialized
            CFLAGS_STDS             += -Wformat-type-confusion
            CFLAGS_STDS             += -Widiomatic-parentheses
            CFLAGS_STDS             += -Wloop-analysis
            CFLAGS_STDS             += -Wshift-sign-overflow
            CFLAGS_STDS             += -Wshorten-64-to-32
            CFLAGS_STDS             += -Wstrict-aliasing=2
            CFLAGS_STDS             += -Wstrict-overflow=5
            CFLAGS_STDS             += -Wtautological-constant-in-range-compare
            CFLAGS_STDS             += -Wthread-safety
            CFLAGS_STDS             += -Wunreachable-code-aggressive
            CFLAGS_STDS             += -Wunused
            CFLAGS_STDS             += -Wunused-argument
            CFLAGS_STDS             += -Wunused-but-set-parameter
            CFLAGS_STDS             += -Wunused-but-set-variable
            CFLAGS_STDS             += -Wunused-comparison
            CFLAGS_STDS             += -Wunused-const-variable
            CFLAGS_STDS             += -Wunused-exception-parameter
            CFLAGS_STDS             += -Wunused-function
            CFLAGS_STDS             += -Wunused-label
            CFLAGS_STDS             += -Wunused-local-typedefs
            CFLAGS_STDS             += -Wunused-macros
            CFLAGS_STDS             += -Wunused-parameter
            CFLAGS_STDS             += -Wunused-value
            CFLAGS_STDS             += -Wunused-variable
            CFLAGS_STDS             += -Wunused-volatile-lvalue
        endif
    else
    	# Disable all manners of warnings, even default ones.
    	CFLAGS_STDS                 += -w
    endif
    
    CFLAGS_OPTS                     += -fPIC
    
    # These are needed anyway.
    ifeq ($(D),1)
        CFLAGS_OPTS                 += -O0 -g3 -ggdb3
        CFLAGS_OPTS                 += -fno-omit-frame-pointer
    else
        CFLAGS_OPTS                 += -O3
        CFLAGS_OPTS                 += -ffunction-sections
        CFLAGS_OPTS                 += -fdata-sections
        CFLAGS_OPTS                 += -fmerge-all-constants
    endif
    ifeq ($(A),1)
        CFLAGS_OPTS                 += -fsanitize=address,leak,undefined
    endif
    
    # Add more include directories to your liking
    CFLAGS_INCS                     += -I.
    
    # Assemble them all into a singfle
    CFLAGS                          += $(CFLAGS_DEFS)
    CFLAGS                          += $(CFLAGS_INCS)
    CFLAGS                          += $(CFLAGS_STDS)
    CFLAGS                          += $(CFLAGS_OPTS)
    
    LDFLAGS                         += -Wl,--as-needed
    LDFLAGS                         += -Wl,--gc-sections
    LDFLAGS                         += -Wl,--no-undefined
    LDFLAGS                         += -Wl,-z,defs
    LDFLAGS                         += -Wl,-z,nocombreloc
    LDFLAGS                         += -Wl,-z,now
    LDFLAGS                         += -Wl,-z,relro
    
    
    # The $(Q)$(CC) has a real TAB before it, not space. If you copy paste from Lemmy, you might end up with spaces.
    program: program.c
    	$(Q)$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
    
    .DEFAULT_GOAL := all
    .PHONY: all
    all: program
    
    .PHONY: clean
    # The space before $(Q)$(RM) is real TAB as well.
    clean:
    	$(Q)$(RM) program
    
  • source
  • parent
  • context
  • [–] 1 point 4 months ago* (2 children)

    For 1, I'd like for you to think you're telling a story, one that is really air tight and unambiguous. If you think putting all the calls in main() can help you tell that story, then so be it.

    For 2, of late industrial software development has largely moved to keeping variables as local as possible so its easier to express provenance. This has been the case for at least a good 3-4 decades now.

    At no extra cost, I'd like to give you an unsolicited advice. If you're using modern, real C compilers like gcc or clang, I'd strongly recommend you pass -Wpedantic -pedantic-errors -Wall -Wextra on the command line.

    gcc -Wpedantic -pedantic-errors -Wall -Wextra -Werror program.c
    

    and if you're using clang, also pass -Wmost along with the rest. Learning C is one thing. But following the standard rules right from the start, it will save you from unexpected grief later on. If you try compiling your code under these flags now, it will refuse to compile. I'd suggest you try fixing as much as you can, then go back to compiling the normal way until you get a hang of things.

    ❀️ 1
  • source
  • [–] [S] 2 points 4 months ago (1 child)

    Thank you so much for this! I'll have to digest your comments slowly, since I don't have any knowledge of or experience with enums, structs or function pointers. YET. 😁

    Let me know if you have ko-fi or the likes and I'll buy you a cup of coffee!

  • source
  • parent
  • context
  • [–] 2 points 4 months ago (2 children)

    I think this is more about readability and style. I have looked at your codei and changed a few thingsπŸ˜…, maybe this helps you. This is how I would do it: (I am as well far from expert level in C)

    CODE

    #include <limits.h> //  added for INT_MIN
    #include <stdio.h>
    #include <stdlib.h> // added for exit()
    #include <string.h>
    
    // you use scanf getchar and fgets, I would  choose one
    //  Function declarations, also chooose your naming convention!
    void prompt_choice_main(char *choice);
    void prompt_choice_main_again(void);
    void prompt_input(void);
    int prompt_choice_trim(void);
    void (*execute_choice[])(void);
    void trim_numbers(void);
    void trim_whitespace(void);
    void trim_letters(void);
    void trim_special(void);
    void specify_special(void);
    void print_result(char input[]);
    
    // Global variables; I would put as much of these as parameters/structs or
    // return values because of clearer ownership and readability
    char choice_detail = 0x00;
    char choice_special = 0x00;
    char input[1000] = "";
    char previous_input[1000] = "";
    
    // Remove specific numbers, letters, punctuation or whitespace characters from
    // input.
    int main() {
      printf(
          "\nWelcome! This program trims text by removing unwanted characters.\n");
      while (1) {
        char choice_main = 0x00;
        prompt_choice_main(&choice_main);
        if (choice_main == 'T') {
          prompt_input();
          int choice_trim = prompt_choice_trim();
          execute_choice[choice_trim]();
          print_result(input);
        } else if (choice_main == 'P') {
          snprintf(input, 1000, "%s",
                   previous_input); // i would prefer snprintf instead of sprintf
                                    // because of safety
          printf("\nYou are trimming previously trimmed text: %s\n", input);
          int choice_trim = prompt_choice_trim();
          execute_choice[choice_trim]();
          print_result(input);
        } else if (choice_main == 'E') {
          printf("\nGoodbye!\n");
          exit(0);
        }
      }
      return 0;
    }
    
    // Function definitions
    void prompt_choice_main(char *choice_main) {
      while (1) {
        printf("\nPress T and ENTER to trim text");
        if (strlen(previous_input) != 0) {
          printf(", P for previously trimmed text ");
        }
        printf(" or E and ENTER to exit: ");
        *choice_main = getchar();
        while (getchar() != '\n') {
        }
        if (*choice_main == 'T' || *choice_main == 'E' || *choice_main == 'P') {
          break;
        } else {
          printf("\nInvalid input!\n");
        }
      }
    }
    
    void prompt_input(void) {
      printf("\nEnter the text that you would like to trim and press ENTER: ");
      size_t c = 0;
      do
        input[c++] = getchar();
      while (input[c] != '\n');
      input[c] = '\0';
    }
    
    int prompt_choice_trim(void) {
      while (1) {
        int choice_trim = 0;
        printf("\nWhat would you like to trim?\n1) Numbers (1, 2, 3...)\n2) "
               "Whitespace (space, tab or newline) \n3) Letters (A,B,C... "
               "a,b,c...)\n4) Special characters (!,?, . , ...)\nType one of the "
               "above numbers and press ENTER: ");
        char c;
        c = getchar();
        choice_trim = c - '0';
        while (getchar() != '\n') {
        }
        if (choice_trim < 1 || choice_trim > 4) {
          printf("\nInvalid choice!\n");
        } else {
          return choice_trim - 1;
        }
      }
      // INFO: Through the return type you can do a bit of error handling i you did
      // not already now
      return INT_MIN;
    }
    
    // Here I would use a function pointer, but your version is also perfectly fine
    void (*execute_choice[])(void) = {
        trim_numbers,    // 123 etc
        trim_whitespace, // space, tab, newline
        trim_letters,    // ABC..., abc...
        trim_special,    // ! ? , . etc.
    };
    
    // I would not replace characters with 0x18 but instead keep chars which a valid
    // and delete chars which are unvalid in a
    void trim_numbers(void) {
      size_t w = 0;
      for (size_t r = 0; input[r] != '\0'; r++) {
        if (input[r] != '0' && input[r] != '1' && input[r] != '2' &&
            input[r] != '3' && input[r] != '4' && input[r] != '5' &&
            input[r] != '6' && input[r] != '7' && input[r] != '8' &&
            input[r] != '9') {
          input[w++] = input[r];
        }
      }
      input[w] = '\0';
      return;
    }
    
    void trim_whitespace(void) {
      while (1) {
        printf("\nType S to trim SPACE, T to trim TAB, N to trim NEWLINE or A to "
               "trim all whitespace: ");
        scanf("%c", &choice_detail);
        while (getchar() != '\n') {
        }
        if (choice_detail == 'S' || choice_detail == 'T' || choice_detail == 'A') {
          break;
        } else {
          printf("\nInvalid input!\n");
        }
      }
      int n = 0;
      for (n = strlen(input) - 1; n >= 0; n--) {
        if (choice_detail == 'S') {
          if (input[n] == 0x20) {
            input[n] = 0x18;
          }
        } // space
        else if (choice_detail == 'T') {
          if (input[n] == 0x09) {
            input[n] = 0x18;
          }
        } // tab
        else if (choice_detail == 'N') {
          if (input[n] == 0x0A) {
            input[n] = 0x18;
          }
        } // newline
        else if (choice_detail == 'A') {
          if (input[n] == 0x20 || input[n] == 0x09 || input[n] == 0x0A) {
            input[n] = 0x18;
          }
        }
      }
      return;
    }
    
    void trim_letters(void) {
      while (1) {
        printf("\nType U to trim uppercase letters, L to trim lowercase letters "
               "or A to trim all letters: ");
        scanf("%c", &choice_detail);
        while (getchar() != '\n') {
        }
        if (choice_detail == 'U' || choice_detail == 'L' || choice_detail == 'A') {
          break;
        } else {
          printf("\nInvalid input!\n");
        }
      }
      int n = 0;
      for (n = strlen(input) - 1; n >= 0; n--) {
        if (choice_detail == 'U') {
          if (input[n] >= 0x41 && input[n] <= 0x5A) {
            input[n] = 0x18;
          }
        } // Uppercase
        else if (choice_detail == 'L') {
          if (input[n] >= 0x61 && input[n] <= 0x7A) {
            input[n] = 0x18;
          }
        } // Lowercase
        else if (choice_detail == 'A') {
          if (input[n] >= 0x41 && input[n] <= 0x5A ||
              input[n] >= 0x61 && input[n] <= 0x7A) {
            input[n] = 0x18;
          }
        }
      }
      return;
    }
    
    void trim_special(void) {
      while (1) {
        printf("\nType A to trim all special characters or S to specify which "
               "character to remove: ");
        scanf("%c", &choice_detail);
        while (getchar() != '\n') {
        }
        if (choice_detail == 'A' || choice_detail == 'S') {
          break;
        } else {
          printf("\nInvalid input!\n");
        }
      }
      int n = 0;
      for (n = strlen(input) - 1; n >= 0; n--) {
        if (choice_detail == 'A') {
          if (input[n] >= 0x21 && input[n] <= 0x2F ||
              input[n] >= 0x3A && input[n] <= 0x40 ||
              input[n] >= 0x5B && input[n] <= 0x60 ||
              input[n] >= 0x7B && input[n] <= 0x7E) {
            input[n] = 0x18;
          }
        } // All whitespace
      }
      if (choice_detail == 'S') {
        specify_special();
      } // Let user specify character.
      return;
    }
    
    void specify_special(void) {
      while (1) {
        printf("\nEnter special character to trim and press ENTER: ");
        scanf("%c", &choice_special);
        while (getchar() != '\n') {
        }
        if (choice_special >= 0x21 && choice_special <= 0x2F ||
            choice_special >= 0x3A && choice_special <= 0x40 ||
            choice_special >= 0x5B && choice_special <= 0x60 ||
            choice_special >= 0x7B && choice_special <= 0x7E) {
          break;
        } else {
          printf("\nNot a special character!\n");
        }
      }
      int n = 0;
      for (n = strlen(input) - 1; n >= 0; n--) {
        if (input[n] == choice_special) {
          input[n] = 0x18;
        }
      }
      return;
    }
    
    void print_result(char input[]) {
      printf("\nTrimmed text:\n\n%s\n", input);
      sprintf(previous_input, "%s", input); // Save trimmed text for reuse.
      return;
    }
    
    // TODO
    //  Create error handling when trimming non existing characters.
    //  Replace characters (uppercase/lowercase, user selected, etc).
    

    ❀️ 1
  • source
  • [–] [S] 2 points 5 months ago* (1 child)

    I gave it some further thought and I might be completely misguided to try and max throughput. Datagrams are completely connectionless and therefore can’t know if your router’s send buffer is full or not, unless I’m missing something internal between the kernel and the router that makes sendto block (which AFAIK only happens when the socket’s send buffer is full). Therefore most β€œextra” datagrams I send would just be dropped anyways. I know ICMP was a dated method of congestion control but have no idea if it would still be in use for simple pings.

    Edit: apparently source quench is a thing but still, no clue if the kernel intercepts this or if it is even sent in 2026 due to deprecation

  • source
  • parent
  • context
  • [–] [S] 2 points 5 months ago* (3 children)

    Is there any reason why it has to be a raw socket, rather than a dgram icmp socket? (which allows you to run as non-root) Silly me for not realizing I only needed one socket. Ofc my speed would only be at most 1-2Mbps. I currently track outgoing pings in memory so I would probably need to keep a single large circular array instead for timeout purposes -- not too different from what I'm doing currently.

  • source
  • parent
  • context
  • [–] 4 points 5 months ago

    Check out GLIBC, the standard libc implementation for GNU/Linux, or the easier to read, minimalist, alternative libc MUSL.

    Bootlin, runs the Elixir Code Cross Referencer that lets you easily hop between files. For instance, Here is the code for printf in GLIBC.

  • source
  • [–] 3 points 5 months ago (4 children)

    The streamlined way to do this is raw sockets. You only need one, and you can just craft all of the ICMP requests and send them continuously. Then in a separate thread you can read all the replies.

  • source
  • [–] 4 points 5 months ago

    I dunno if this helps, but this screenshot shows the memory view of a program with a string. The hex representation in the middle is what is actually stored in memory. Each pair is one byte/char, and that is what your input[n] <= 0x39 is comparing against.

    ❀️ 1
  • source
  • parent
  • context
  • [–] 7 points 5 months ago

    I C (and most other languages) you can enter a value like thirty-two using many different number bases: β€˜ β€˜ as a char, 32 in decimal, 0x20 is hex, 040 in octal or 0b00010000. All of these mean thirty-two, just written differently using different characters.

    Note that each of these representations use different digits and/or letters to specify the same number - the mathematical entity thirty-two. These letters and digits are characters stored as numbers, eg the digit zero (0) is coded in ASCII as 48.

    In the end, the C compiler will compile the text β€˜32’, β€˜0x40’ or β€˜0b00010000’ into binary and store it in memory the single byte where the 5th bit set to 1 and the others to 0. This is the way computer memory is used to represent the number, and it’s linked to the way the electric circuits do things like add numbers together.

    The compiler is the thing that understands that the characters like β€˜3’, β€˜+’ or β€˜F’ and works out what they represent (a number, a variable name or a sum). When it understands that, it will turn the code into bits that the CPU can work with, for example passing it to the compare instruction CMP used to test whether the input is less than the number thirty-two.

    This is very meta, and I hope this clarifies rather than confuses. Compilers are notoriously confusing, such as the thought β€˜the compiler compiles the compiler code into an executable that can compile itself’.

  • source
  • [–] 3 points 5 months ago (2 children)

    You are inspecting a byte, which contains 0x39. That it corresponds to "9" doesn't matter at all to the compiler at all. If you were on a system that didn't match "9" <=> 0x39 it would still replace those byte values.

  • source
  • [–] 11 points 5 months ago* (last edited 5 months ago) (1 child)

    There are multiple implementations, the one often found on linux is glibc, you can find the source code for that online. printf is going to be a small wrapper for vfprintf though, so that is where you will probably find what you are looking for

  • source
  • [–] 5 points 5 months ago* (last edited 5 months ago) (1 child)

    Been a while since I've done this kind of thing, but you should be able to create a pool of sockets primed with the first batch of IP addresses, then send the packets.

    On reply or timeout, you don't destroy the socket and create a new one. Instead you alter the connection information in the socket descriptor in the existing socket to point to the next address to check, and then go again.

  • source
  • view more: β€Ή prev next β€Ί