Bash builtin (not SED!) search and replace using octal values - arrays

I'm having problems getting my code to work:
for (( c=1; c<=$DirsArrCnt; c=c+$OneDirArrCnt )); do
# Replace every occurence of "/" (ASCII d47 o057) in path with "^A" (ASCII 1)
Hold="${DirsArr[$c]}"
DirsArr[c]="${Hold//\057/\001}"
done
Originally I skipped the Hold variable and used the array element directly but took that out thinking it was the problem.
Am I specifying the octal value correctly? I believe 57 is the octal value for "/" right?

I think this is what you want :
DirsArr[c]="${Hold//$'\057'/$'\001'}"
The syntax you use interprets \0 as a literal 0 (i.e. does nothing different compared to not using the backslash). You need the C-style string to have your numeric code interpreted by the shell.

Related

Writing one byte in struct variable

I'm trying to modify one byte of my structure with the following code :
struct example *dev;
PRINT_OPAQUE_STRUCT(dev);
sprintf((char*) dev + 24, "%x",1);
PRINT_OPAQUE_STRUCT(dev);
The PRINT_OPAQUE_STRUCT is just printing the content of the structure, and is defined in this other topic :
Print a struct in C
The output of this program is :
d046f64f20b3fb4f00000000e047f64f00000000ffffffff000000
d046f64f20b3fb4f00000000e047f64f00000000ffffffff310000
I don't know why I have the value "31" written and not the value "01" as wanted. I tried to replace the second argument of sprintf with "%01x" but it didn't change anything. Anyone knows why?
Thanks!
Well, you are formatting the value 1 as a string. That's what sprintf does. 0x31 is the character code for the character '1'. If you just want to write the byte value 0x01 into your struct, then you don't need sprintf. Just do this:
*((char*)dev + 24) = 1;
or (the same, but with slightly different syntax):
((char*)dev)[24] = 1;
Note also, like one comment below says, sprintf will not just write one single byte. Since it writes a string, and C strings are null-terminated, it will also write a null character ('\0', 0x00) right after the '1'.
I don't know why I have the value "31" written and not the value "01" as wanted.
The reason you see 31 is that your chain of functions interprets the value 1 twice:
First, sprintf interprets it as a character representing a hex digit
Second, PRINT_OPAQUE_STRUCT interprets the value again, now as a hex number
Essentially, sprintf converts 1 to its character representation, which is '1'. On your system, its code is 0x31, hence the output that you get.
You need to remove one of these two interpretations to get your code to print 1. To remove the first interpretation, simply assign 1 to the pointer, like this:
((char*)dev)[24] = 1;
To remove the second interpretation, print with %c instead of %x in PRINT_OPAQUE_STRUCT (which may not be possible).

Hex value in C string

I need to prepare constant array of ANSI C strings that contains bytes from range of 0x01 to 0x1a. I made custom codepage, so those values represents different characters (i.e. 0x09 represents Š). I'd like to initialise the array in that way:
static const char* brands[] = {
"Škoda",
//etc...
};
How can I put 0x09 instead of Š in "Škoda"?
Recommend not using "\x09koda", use octal or spaced strings.
The problem is that hexadecimal escape sequences are not limited in their length. So if the next char is a hexadecimal character, problems occur. Use octal, which is limited to 3. Or use separated strings. The compiler will concatenate then, but the escape sequence will not accidentally run too far.
// problematic
"\x09Czech"
^^^^^--- The escape sequence is \x09C, but \0x09 was hoped for
// recommend octal
"\0111234"
^^^^--- The escape sequence is \011
// recommend spaced strings
"\x09" "Czech"
Very simple
"Škoda" -> "\x09koda"
How can I put 0x09 instead of Š in "Škoda"?
"\x09koda"
Have a look at escape squences - i.e \x09
For hex escape you want to use the \Xnnn, for octal just \nnn and for unicode \Unnnn

how to include hex value in string using sprintf

i want to include value of i hex format in c.
for(i=0;i<10;i++)
sprintf(s1"DTLK\x%x\xFF\xFF\xFF\xFF\xFF\xFF",i);
but the above code outputs an error: \x used with no following hex digits
Pls any one suggest me a proper way....
Supposing you don't want to literally have \x00..\x0A, but the corresponding byte, you need
sprintf(s1, "DTLK%c\xFF\xFF\xFF\xFF\xFF\xFF",i);
while inserting \x%x would be at the wrong abstraction level...
If, OTOH, you really want to literally have the hex characters instead of the bytes with the named hey characters as their representation, the other answers might be more helpful.
You need to escape the slash on front of the \x:
sprintf(s1"DTLK\\x%x\xFF\xFF\xFF\xFF\xFF\xFF",i);
// ^------- Here
Depending on what output you would like to achieve, you may need to escape the remaining slashes as well.
Currently, the snippet produces a sequence of six characters with the code 0xFF. If this is what you want, your code fragment is complete. If you would like to see a sequence of \xFF literals, i.e. a string that looks like \x5\xFF\xFF\xFF\xFF\xFF\xFF when i == 5, you need to escape all slashes in the string:
sprintf(s1"DTLK\\x%x\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF",i);
// ^ ^ ^ ^ ^ ^ ^
Finally, if you would like the value formatted as a two-digit hex code even when the value is less than sixteen, use %02x format code to tell sprintf that you want a leading zero.
\x expects a hex value like \xC9.
If you want to include \x in your output, you need to escape \ with \\:
sprintf(s1"DTLK\\x%x\xFF\xFF\xFF\xFF\xFF\xFF",i);
sprintf(s1"DTLK\\x%x\xFF\xFF\xFF\xFF\xFF\xFF",i);
// ^------- Here
Depending on what output you would like to achieve, you may need to escape the remaining slashes as well.
Currently, the snippet produces a sequence of six characters with the code 0xFF. If this is what you want, your code fragment is complete. If you would like to see a sequence of \xFF literals, i.e. a string that looks like \x5\xFF\xFF\xFF\xFF\xFF\xFF when i == 5, you need to escape all slashes in the string:
sprintf(s1"DTLK\\x%x\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF",i);
// ^ ^ ^ ^ ^ ^ ^
Finally, if you would like the value formatted as a two-digit hex code even when the value is less than 16, use %02x format code to tell sprintf that you want a leading zero.

Make R use C notation when escaping terminals

Not sure I am using the right terminology here, but I need the print or deparse methods use C notation (e.g. "\x05" instead of "\005" ) when escaping bytes out of the regular character set.
x <- "This is a \x05 symbol"
print(x)
[1] "This is a \005 symbol"
Is there a native way to accomplish this?
I need this for generating BSON: http://bsonspec.org/#/specification. All of the examples explicitly use \x05 notation.
Hacking into the internals of print seems a bad idea. Instead I think you should do the string escaping yourself, and eventually use cat to print the string without any extra escaping.
You can use encodeString to do the initial escaping, gregexpr to identify octal \0.. escapes, strtoi to convert strings representing octal numbers to those numbers, sprintf to print numbers in hexadecimal, and regenmatches to operate on the matched parts. The whole process would look something like this:
inputString <- "This is a \005 symbol. \x13 is \\x13."
x <- encodeString(inputString)
m <- gregexpr("\\\\[0-3][0-7][0-7]", x)
charcodes <- strtoi(substring(regmatches(x, m)[[1]], 2, 4), 8)
regmatches(x, m) <- list(sprintf("\\x%02x", charcodes))
cat(x, "\n")
Note that this approach will convert octal escapes like \005 to hexadecimal escapes like \x05, but other escape sequences like \t or \a won't be affected by this. You might need more code to deal with those as well, but the above should contain all the ingredients you need.
Note that the BSON specification you refer to almost certainly meant raw bytes, so as long as your string contains a character with code 5, which you can write as "\x05" in your input, and you write that string to the desired output in binary mode, it shouldn't matter at all how R prints that string to you. After all, octal \005 and hexadecimal \x05 are just two representations of the same byte you'll write.
Does cat suit your needs? Note, you have to escape the backslash:
> x <- "This is a \\x05 symbol\n"
> cat(x)
This is a \x05 symbol

Switching numbers inside string

I got a string, that inside it has:
2#0.88315#1#1.5005#true#0.112 and it keep going...
I need to switch every number thats 2 or bigger, to 1,
so I wrote this :
for (i = 0 ; i < strlen(data) ; i++)
{
if (data[i] >= 50 && data[i] <= 57) // If it's a number
{
data[i] = '1'; // switch it to one
while (data[i] >= 48 && data[i] <= 57)
{
i++;
}
}
}
The problem is, that it makes numbers like 0.051511 as 1.111111 too...
Because it doesnt look at a double as one number, but every number seperatly...
How can I do it ?
Thanks
To clarify the question since it is unclear, you want to have the following input:
"2#0.88315#1#1.5005#true#0.112"
To be modified to be the following:
"1#0.88315#1#1#true#0.112"
Your problem is that you need to parse each number into a float value to do any sort of comparison. Either this, or you will need to manually parse it by checking for a '.' character. Doing it manually is rigid, error-prone and unnecessary because the C standard library provides functions which can help you.
Since this is homework, I'll give you some tips on how to approach this problem instead of the actual solution. What you should do is try to write a solution with these steps and if you get stuck, edit the original question with the code you wrote, where it is failing and why you think it is failing.
Your first step is to tokenise the input into the following:
"2"
"0.88315"
"1"
"1.5005"
"true"
"0.112"
This can be done by iterating through the string and either splitting it or using the pointer after which a '#' character occurs. Splitting the string can be done with strtok. However, strtok will split the string by modifying it which is not necessarily needed in our case. The simpler method is simply to iterate through the string and stop each time after a '#' character is reached. The input would then be tokenised to the following:
"2#0.88315#1#1.5005#true#0.112"
"0.88315#1#1.5005#true#0.112"
"1#1.5005#true#0.112"
"1.5005#true#0.112"
"true#0.112"
"0.112"
Some of these substrings do not start with a string which represents a float. You will need to determine which of them do. To do this, you can attempt to parse the front of each string as a float. This can be done with sscanf. After parsing the floats, you will be able to do the comparison you want to.
You are trying to modify the string into a different length so when replacing a float value by a '1', you need to check the length of the original value. If it is longer than 1 character, you will have to shift the subsequent characters forward. For example:
"3.423#1"
If you parsed the first token and found it to be > 2, you would replace the first character with a '1'. This result in:
"1.423#1"
You then still need to delete the rest of that token by shifting the rest of the string down to get:
"1#1"
It looks like you're comparing a char and an int in your if statements.
You should figure out why this matters and compensate for it.
You're comparing the characters in the string one at a time. If you need to consider everything between the "#" symbols as one number, this won't work. Try to get these numbers into an array, cast them to a double, and then do your comparison against 2.

Resources