-1

I'm trying to write() hexadecimal representation of \n without any success.

The code I have ft_putstr_non_printable.c:

#include <unistd.h>

void    ft_putstr_non_printable(char *str)
{
    int             i;
    unsigned char   a;
    char            c;

    i = 0;
    a = 0x0;
    while (str[i] != '\0')
    {
        if (str[i] <= 31 || str[i] == 127)
        {
            a = str[i];
            write(1, &a, 1);
        }
        else
        {
            c = str[i];
            write(1, &c, 1);
        }
        i++;
    }
}

And main.c:

#include <stdio.h>
#include <string.h>

#include "ft_putstr_non_printable.c"

int main(void)
{
    char a[] = "\n au revoir\a";
    char b[] = "omellette du fromage\b";
    char c[] = "coeuf@ca6va\e fef";
    char d[] = " Batata \x7F rfg";
    char e[] = "roquefort`[e{forte-e_tem,bolor \n feff";
    char f[] = " we 9are 78familly \x1F rgfenf";

    ft_putstr_non_printable(a);
    ft_putstr_non_printable(b);
    ft_putstr_non_printable(c);
    ft_putstr_non_printable(d);
    ft_putstr_non_printable(e);
    ft_putstr_non_printable(f);
}

Am I doing something wrong? How do I get \x0a?

Edit: I can't use printf(). I'm limited to write().

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Miguel M
  • 302
  • 3
  • 16
  • 2
    Well, `write(1, &a, 1);` and `write(1, &c, 1);` are basically identical, so you're not going to accomplish it that way. `write` is not like `printf`. `write` is a low-level function that just writes raw bytes. To get `\n` to come out as `0a` or `0x0a` means you're going to have to write two or more bytes, and this in turn means you're going to have to construct those two or more bytes somehow. The simplest way is `sprintf`, if that's allowed. – Steve Summit Jul 08 '21 at 19:40
  • To print the hexadecimal representation of `\n` (well any character) do: `printf("%02x", '\n');` – Support Ukraine Jul 08 '21 at 19:42
  • Study the string library. – TomServo Jul 08 '21 at 19:42
  • If you're trying to avoid `printf` for whatever reason, you're looking for a function such as `itoa` that converts a number to a string. Then you'll have to explicitly print each character – Jakob Lovern Jul 08 '21 at 19:42
  • @JakobLovern cant use itoa either only func I can use is write – Miguel M Jul 08 '21 at 19:45
  • Given that itoa isn't a POSIX standard, no wonder you can't use it. You can, however, write your own implementation inside `ft_putstr_non_printable` – Jakob Lovern Jul 08 '21 at 19:46
  • 2
    Suggestion: Never `#include` `.c` files, like you do in `#include "ft_putstr_non_printable.c"` – Ted Lyngmo Jul 08 '21 at 19:50
  • Also, `\e` is a non-standard escape sequence. What do you want with that? – Ted Lyngmo Jul 08 '21 at 19:59

5 Answers5

5

Instead writing one character when str[i] is out of the printable range, form a little string and write that.

    // if (str[i] <= 31 || str[i] == 127)
    if (str[i] <= 31 || str[i] >= 127) {
        unsigned char a = str[i];
        char buf[5];
        int len = sprintf(buf, "\\x%02X", a);
        // write(1, &a, 1);
        write(1, buf, len);
    }

I'm limited to write()

If sprintf() not available:

        // int len = sprintf(buf, "\\x%02X", a);
        buf[0] = '\\'; 
        buf[1] = 'x'; 
        buf[2] = "0123456789ABCDEF"[a/16];
        buf[3] = "0123456789ABCDEF"[a%16];
        buf[4] = '\0';
        len = 4;

Advanced:

char may be unsigned, so values above 127 are possible.

To well reverse the process it might make sense to print the \\ in hex.

    if (str[i] <= 31 || str[i] >= 127 || str[i] == '\\') {
0___________
  • 60,014
  • 4
  • 34
  • 74
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Notice:

I recommend the answer from chux - Reinstate Monica due to the nice conversion from 0..15 to hex.

See https://stackoverflow.com/a/68307913/4386427

I'll leave this answer as-is just in case someone should prefer this code-wise longer way of doing the conversion

Answer

Given all your restriction (which prevents normal code), you may be looking for:

char a = '\n';  // Or any other char
char h;
unsigned char tmp;

tmp = a;
tmp = tmp / 16;
if (tmp < 10)
{
    h = '0' + tmp;
}
else
{
    h = 'a' + tmp - 10;
}
write(1,&h,1);

tmp = a
tmp = tmp % 16;
if (tmp < 10)
{
    h = '0' + tmp;
}
else
{
    h = 'a' + tmp - 10;
}
write(1,&h,1);

Output

0a
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
1

I have wrapped the hex output into [] to make them mo distinct.

#include <unistd.h>

void    ft_putstr_non_printable(const char *s)
{
    const char *hex = "0123456789ABCDEF";
    const unsigned char *str = s;
    while (*str)
    {
        if (*str <= 31 || *str >= 127)
        {
            char hexrep[] = {'[','0','x', hex[*str >> 4], hex[*str & 0x0f],']'};
            write(1, hexrep, sizeof(hexrep));
        }
        else
        {
            write(1, str, 1);
        }
        str++;
    }
    write(1, (char[]){'\n'},1);
}


int main(void)
{
    char a[] = "\n au revoir\a";
    char b[] = "omellette du fromage\b";
    char c[] = "coeuf@ca6va\e fef";
    char d[] = " Batata \x7F rfg";
    char e[] = "roquefort`[e{forte-e_tem,bolor \n feff";
    char f[] = " we 9are 78familly \x1F rgfenf";

    ft_putstr_non_printable(a);
    ft_putstr_non_printable(b);
    ft_putstr_non_printable(c);
    ft_putstr_non_printable(d);
    ft_putstr_non_printable(e);
    ft_putstr_non_printable(f);
}

https://godbolt.org/z/zq7sPfM6q

Output:

[0x0A] au revoir[0x07]
omellette du fromage[0x08]
coeuf@ca6va[0x1B] fef
 Batata [0x7F] rfg
roquefort`[e{forte-e_tem,bolor [0x0A] feff
 we 9are 78familly [0x1F] rgfenf

If you want to have \xHH format suimply change one line to:

            char hexrep[] = {'\\','x', hex[*str >> 4], hex[*str & 0x0f]};

https://godbolt.org/z/6GonenfK7

Output:

\x0A au revoir\x07
omellette du fromage\x08
coeuf@ca6va\x1B fef
 Batata \x7F rfg
roquefort`[e{forte-e_tem,bolor \x0A feff
 we 9are 78familly \x1F rgfenf
0___________
  • 60,014
  • 4
  • 34
  • 74
1

Yet another option:

void ft_putstr_non_printable(const char *str) {
    static const char hex[] = "0123456789ABCDEF";
    int outfd = fileno(stdout);
    char buf[4] = {'0', 'x'};
    unsigned char ch; // used to convert the usually signed `char` to unsigned

    for(; (ch = *str) != '\0'; ++str) {    // loop until null terminator
        if (ch < ' ' || ch > '~') {        // outside printable ASCII range?
            // pick the last two chars in `buf` from the hex array:
            buf[2] = hex[ch >> 4];         // the high nibble
            buf[3] = hex[ch & 0xF];        // the low nibble
            write(outfd, buf, sizeof buf); // ex: writes 0x7F if ch == 127
        } else {
            write(outfd, &ch, 1);
        }
    }
    ch = '\n';
    write(outfd, &ch, 1);
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

What does write() actually do? It's defined in <unistd.h> as:

ssize_t write(int fildes, const void *buf, size_t nbyte);

It writes to a filedescriptor. It writes from a buffer and writes nbytes bytes from that memory location.

Thus when you pass &i to it, write just sees memory addresses. It doesn't see an int. Likewise with &c. What you need to do is turn each character (as a number) into a string representing the character. The code for ft_putstr_non_printable would look something like:

void ft_putstr_non_printable(char *str)
{
    int             i;
    unsigned char   a;
    i = 0;
    const char[] hexchars = "0123456789ABCDEF";
    for (int ii = 0;str[i]!='\0';ii++)
    {
        a = str[i];
        if (str[i]> 31 && str[i] < 127)
            write(1, &a, 1);
        else
        {
            write(1,'[',1);
            if (a<=16
            while (a!=0)
            {
                write(1,hexchars+(a%16),1);
                a/=16;
            }
            write(1,']',1);
        }
    }
}

This is basically how itoa() works. @4386427's answer is more elegant for single byte characters, but I wanted to explicitly show how one would do it in a while loop.

Jakob Lovern
  • 1,301
  • 7
  • 24
  • Code is printing the hex value in little endian order. Was that intended? Note: `while (a!=0) { write(1,hexchars+(a%16),1); a/=16; }` write 1 or 2 characters - that leads to ambiguity if a 1 character hex output is followed by a digit character. – chux - Reinstate Monica Jul 08 '21 at 20:53
  • Unfortunately, for n-ary escape codes that ambiguity is inevitable. (It's basically the String problem all over again: Either prepend with the number of characters or append with a code ender.) I've chosen to adopt bracketed notation. It's not as good but it gets the job done. – Jakob Lovern Jul 08 '21 at 22:22
  • As for endianness, I don't know how to fix that without either an endian reverse operation or complicated modulo math... Or pushing to a stack similar to what you did. – Jakob Lovern Jul 08 '21 at 22:23