| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "pegasus_utils.h" |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| #include <netdb.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| namespace pegasus { |
| namespace utils { |
| |
| void addr2host(const ::dsn::rpc_address &addr, char *str, int len /* = 100*/) |
| { |
| struct sockaddr_in addr2; |
| addr2.sin_addr.s_addr = htonl(addr.ip()); |
| addr2.sin_family = AF_INET; |
| if (getnameinfo((struct sockaddr *)&addr2, |
| sizeof(sockaddr), |
| str, |
| sizeof(char *) * len, |
| nullptr, |
| 0, |
| NI_NAMEREQD)) { |
| inet_ntop(AF_INET, &(addr2.sin_addr), str, 100); |
| } |
| } |
| |
| size_t |
| c_escape_string(const char *src, size_t src_len, char *dest, size_t dest_len, bool always_escape) |
| { |
| const char *src_end = src + src_len; |
| size_t used = 0; |
| |
| for (; src < src_end; src++) { |
| unsigned char c = *src; |
| if (always_escape) { |
| if (dest_len - used < 5) // space for four-character escape + \0 |
| return (size_t)-1; |
| snprintf(dest + used, 5, "\\x%02X", c); |
| used += 4; |
| continue; |
| } |
| if (dest_len - used < 2) // space for two-character escape |
| return (size_t)-1; |
| switch (c) { |
| case '\n': |
| dest[used++] = '\\'; |
| dest[used++] = 'n'; |
| break; |
| case '\r': |
| dest[used++] = '\\'; |
| dest[used++] = 'r'; |
| break; |
| case '\t': |
| dest[used++] = '\\'; |
| dest[used++] = 't'; |
| break; |
| case '\"': |
| dest[used++] = '\\'; |
| dest[used++] = '\"'; |
| break; |
| case '\'': |
| dest[used++] = '\\'; |
| dest[used++] = '\''; |
| break; |
| case '\\': |
| dest[used++] = '\\'; |
| dest[used++] = '\\'; |
| break; |
| default: |
| // Note that if we emit \xNN and the src character after that is a hex |
| // digit then that digit must be escaped too to prevent it being |
| // interpreted as part of the character code by C. |
| if (c < ' ' || c > '~') { |
| if (dest_len - used < 5) // space for four-character escape + \0 |
| return (size_t)-1; |
| snprintf(dest + used, 5, "\\x%02X", c); |
| used += 4; |
| } else { |
| dest[used++] = c; |
| break; |
| } |
| } |
| } |
| |
| if (dest_len - used < 1) // make sure that there is room for \0 |
| return (size_t)-1; |
| |
| dest[used] = '\0'; // doesn't count towards return value though |
| return used; |
| } |
| |
| inline unsigned int hex_digit_to_int(char c) |
| { |
| /* Assume ASCII. */ |
| assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61); |
| assert(isxdigit(c)); |
| unsigned int x = static_cast<unsigned char>(c); |
| if (x > '9') { |
| x += 9; |
| } |
| return x & 0xf; |
| } |
| |
| // return < 0 means failed |
| static int c_unescape_sequences(const char *source, char *dest) |
| { |
| char *d = dest; |
| const char *p = source; |
| |
| // Small optimization for case where source = dest and there's no escaping |
| while (p == d && *p >= ' ' && *p <= '~' && *p != '\\') { |
| p++; |
| d++; |
| } |
| |
| while (*p != '\0') { |
| if (*p == '\\') { |
| switch (*++p) { // skip past the '\\' |
| case 'n': |
| *d++ = '\n'; |
| break; |
| case 'r': |
| *d++ = '\r'; |
| break; |
| case 't': |
| *d++ = '\t'; |
| break; |
| case '"': |
| *d++ = '\"'; |
| break; |
| case '\'': |
| *d++ = '\''; |
| break; |
| case '\\': |
| *d++ = '\\'; |
| break; |
| case 'x': |
| case 'X': { |
| if (!isxdigit(p[1]) || !isxdigit(p[2])) { |
| return source - p - 1; |
| } |
| unsigned int ch = hex_digit_to_int(p[1]); |
| ch = (ch << 4) + hex_digit_to_int(p[2]); |
| *d++ = ch; |
| p += 2; |
| break; |
| } |
| default: |
| return source - p - 1; |
| } |
| p++; // read past letter we escaped |
| } else if (*p >= ' ' && *p <= '~') { |
| *d++ = *p++; |
| } else { |
| return source - p - 1; |
| } |
| } |
| *d = '\0'; |
| return d - dest; |
| } |
| |
| int c_unescape_string(const std::string &src, std::string &dest) |
| { |
| dest = src; |
| int len = c_unescape_sequences(dest.c_str(), &dest[0]); |
| if (len >= 0 && len < dest.length()) |
| dest.resize(len); |
| return len; |
| } |
| |
| } // namespace utils |
| } // namespace pegasus |