github.com/aldelo/common@v1.5.1/ascii/ascii.go (about) 1 package ascii 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 ) 24 25 // ascii definition 26 // use string(...) to convert the const into string value 27 const ( 28 NUL = 0x00 // '\0' Null 29 SOH = 0x01 // Start of Header 30 STX = 0x02 // Start of Text 31 ETX = 0x03 // End of Text 32 EOT = 0x04 // End of Transmission 33 ENQ = 0x05 // Enquiry 34 ACK = 0x06 // Acknowledgement 35 BEL = 0x07 // '\a' Bell 36 BS = 0x08 // '\b' Backspace 37 HT = 0x09 // '\t' Horizontal Tab 38 LF = 0x0A // '\n' Line Feed 39 VT = 0x0B // '\v' Vertical Tab 40 FF = 0x0C // '\f' Form Feed 41 CR = 0x0D // '\r' Carriage Return 42 SO = 0x0E // Shift Out 43 SI = 0x0F // Shift In 44 DLE = 0x10 // Device Idle 45 DC1 = 0x11 // Device Control 1 46 DC2 = 0x12 // Device Control 2 47 DC3 = 0x13 // Device Control 3 48 DC4 = 0x14 // Device Control 4 49 NAK = 0x15 // Negative Ack 50 SYN = 0x16 // Synchronize 51 ETB = 0x17 // End of Transmission Block 52 CAN = 0x18 // Cancel 53 EM = 0x19 // End of Medium 54 SUB = 0x1A // Substitute 55 ESC = 0x1B // '\e' Escape 56 FS = 0x1C // Field Separator 57 GS = 0x1D // Group Separator 58 RS = 0x1E // Record Separator 59 US = 0x1F // Unit Separator 60 SP = 0x20 // Space 61 DEL = 0x7F // Delete 62 COMMA = 0x2C // Comma 63 COLON = 0x3A // Colon 64 PIPE = 0x7C // Pipe 65 66 ) 67 68 func AsciiToString(i int) string { 69 return string(rune(i)) 70 } 71 72 // GetLRC calculates the LRC value for input string, 73 // returns blank LRC to indicate error condition (see error for reason) 74 // 75 // parameters: 76 // 77 // data = includes the STX and ETX but not LRC if exists 78 // 79 // returns: 80 // 81 // string = LRC string value 82 func GetLRC(data string) (string, error) { 83 if len(strings.Trim(data, " ")) <= 1 { 84 return "", fmt.Errorf("Data is Required for LRC Calculation") 85 } 86 87 // LRC check excludes STX 88 firstChar := data[:1] 89 90 if firstChar == AsciiToString(STX) { 91 data = data[1:] 92 } 93 94 // excluding STX, must be 2 or more chars 95 if len(data) < 2 { 96 return "", fmt.Errorf("Data Must Be 2 Characters or More for LRC Calculation") 97 } 98 99 lrcBytes := []byte(data) 100 lrc := byte(0) 101 102 // loop through each element, XOR product of element and next adjacent element and continue 103 for i, v := range lrcBytes { 104 if i == 0 { 105 lrc = v 106 } else { 107 lrc ^= v 108 } 109 } 110 111 // return lrc value 112 return string(lrc), nil 113 } 114 115 // IsLRCValid checks if the input data that contains the entire string, including STX ETX and LRC, that its LRC is valid for the content of the data 116 func IsLRCValid(data string) bool { 117 if len(data) <= 2 { 118 return false 119 } 120 121 if calcLrc, err := GetLRC(data[:len(data)-1]); err != nil || len(calcLrc) == 0 { 122 return false 123 } else { 124 return data[len(data)-1:] == calcLrc 125 } 126 } 127 128 // IsCreditCardMod10Valid performs modulo 10 credit card number validation 129 func IsCreditCardMod10Valid(cardNumber string) (bool, error) { 130 cardNumber = strings.Trim(cardNumber, " ") 131 132 if len(cardNumber) < 5 { 133 return false, fmt.Errorf("Card Number Must Be Greater or Equal to 5 Digits") 134 } 135 136 if _, err := strconv.ParseUint(cardNumber, 10, 64); err != nil { 137 return false, fmt.Errorf("Card Number Must Be Numeric") 138 } 139 140 source := cardNumber[:len(cardNumber)-1] 141 checkDigit := cardNumber[len(cardNumber)-1:] 142 143 result := 0 144 multiplier := 2 145 146 // loop through each element from right to left, 147 // multiple element by value of 2, and 1 alternating 148 for i := len(source) - 1; i >= 0; i-- { 149 if temp, e := strconv.Atoi(string(source[i])); e == nil { 150 temp *= multiplier 151 152 if multiplier == 2 { 153 multiplier = 1 154 } else { 155 multiplier = 2 156 } 157 158 for { 159 if temp < 10 { 160 result += temp 161 break 162 } 163 164 buf := strconv.Itoa(temp) 165 x, _ := strconv.Atoi(buf[:1]) 166 y, _ := strconv.Atoi(buf[len(buf)-1:]) 167 temp = x + y 168 } 169 } 170 } 171 172 // find the next highest multiple of 10 173 multiplier = result % 10 174 175 if multiplier > 0 { 176 multiplier = result/10 + 1 177 } else { 178 multiplier = result / 10 179 } 180 181 // get check digit 182 result = multiplier*10 - result 183 184 if chk, err := strconv.Atoi(checkDigit); err != nil { 185 return false, fmt.Errorf("Convert Check Digit Failed: %s", err) 186 } else { 187 return chk == result, nil 188 } 189 } 190 191 // EnvelopWithStxEtxLrc will take content data, wrap with STX, ETX, and calculate LRC to append 192 // 193 // contentData = do not include STX, ETX, LRC 194 func EnvelopWithStxEtxLrc(contentData string) string { 195 if len(contentData) == 0 { 196 return "" 197 } 198 199 if contentData[:1] != AsciiToString(STX) { 200 contentData = AsciiToString(STX) + contentData 201 } 202 203 if len(contentData) >= 2 { 204 removeLast := false 205 d := contentData[:2] 206 207 if d[:1] == AsciiToString(ETX) { 208 removeLast = true 209 } else if d[len(d)-1:] != AsciiToString(ETX) { 210 contentData += AsciiToString(ETX) 211 } 212 213 if removeLast { 214 contentData = contentData[:len(contentData)-1] 215 } 216 } else { 217 contentData += AsciiToString(ETX) 218 } 219 220 lrc, _ := GetLRC(contentData) 221 return contentData + lrc 222 } 223 224 // StripStxEtxLrcFromEnvelop removes STX ETX and LRC from envelopment and returns content data. 225 // this method will validate LRC, if LRC fails, blank is returned. 226 // 227 // ignoreValueToLeftOfDelimiterSymbol = if set, any value to left side of this delimiter (along with this delimiter) is excluded from the return result 228 func StripStxEtxLrcFromEnvelop(envelopData string, ignoreValueToLeftOfDelimiterSymbol ...string) string { 229 if len(envelopData) == 0 { 230 return "" 231 } 232 233 if ok := IsLRCValid(envelopData); ok { 234 // remove lrc 235 envelopData = envelopData[:len(envelopData)-1] 236 237 if len(envelopData) == 0 { 238 return "" 239 } 240 241 // remove stx 242 if envelopData[:1] == AsciiToString(STX) { 243 if len(envelopData) > 1 { 244 envelopData = envelopData[1:] 245 } else { 246 return "" 247 } 248 } 249 250 // remove etx 251 if envelopData[len(envelopData)-1:] == AsciiToString(ETX) { 252 if len(envelopData) > 1 { 253 envelopData = envelopData[:len(envelopData)-1] 254 } else { 255 return "" 256 } 257 } 258 259 // if value to left of delimiter is to be excluded 260 if len(ignoreValueToLeftOfDelimiterSymbol) > 0 { 261 if p := strings.SplitN(envelopData, ignoreValueToLeftOfDelimiterSymbol[0], 2); len(p) == 2 { 262 envelopData = p[1] 263 } 264 } 265 266 return envelopData 267 } else { 268 return "" 269 } 270 } 271 272 // ControlCharToWord converts non-printable control char to word 273 func ControlCharToWord(data string) string { 274 data = strings.ReplaceAll(data, AsciiToString(STX), "[STX]") 275 data = strings.ReplaceAll(data, AsciiToString(ETX), "[ETX]") 276 data = strings.ReplaceAll(data, AsciiToString(ETB), "[ETB]") 277 data = strings.ReplaceAll(data, AsciiToString(ACK), "[ACK]") 278 data = strings.ReplaceAll(data, AsciiToString(NAK), "[NAK]") 279 data = strings.ReplaceAll(data, AsciiToString(ENQ), "[ENQ]") 280 data = strings.ReplaceAll(data, AsciiToString(DLE), "[DLE]") 281 data = strings.ReplaceAll(data, AsciiToString(DC1), "[DC1]") 282 data = strings.ReplaceAll(data, AsciiToString(DC2), "[DC2]") 283 data = strings.ReplaceAll(data, AsciiToString(DC3), "[DC3]") 284 data = strings.ReplaceAll(data, AsciiToString(DC4), "[DC4]") 285 data = strings.ReplaceAll(data, AsciiToString(FS), "[FS]") 286 data = strings.ReplaceAll(data, AsciiToString(US), "[US]") 287 data = strings.ReplaceAll(data, AsciiToString(GS), "[GS]") 288 data = strings.ReplaceAll(data, AsciiToString(RS), "[RS]") 289 data = strings.ReplaceAll(data, AsciiToString(BS), "[BS]") 290 data = strings.ReplaceAll(data, AsciiToString(BEL), "[BEL]") 291 data = strings.ReplaceAll(data, AsciiToString(DEL), "[DEL]") 292 data = strings.ReplaceAll(data, AsciiToString(EOT), "[EOT]") 293 data = strings.ReplaceAll(data, AsciiToString(COMMA), "[COMMA]") 294 data = strings.ReplaceAll(data, AsciiToString(COLON), "[COLON]") 295 data = strings.ReplaceAll(data, AsciiToString(PIPE), "[PIPE]") 296 data = strings.ReplaceAll(data, AsciiToString(NUL), "[NULL]") 297 data = strings.ReplaceAll(data, AsciiToString(SOH), "[SOH]") 298 data = strings.ReplaceAll(data, AsciiToString(HT), "[HT]") 299 data = strings.ReplaceAll(data, AsciiToString(LF), "[LF]") 300 data = strings.ReplaceAll(data, AsciiToString(VT), "[VT]") 301 data = strings.ReplaceAll(data, AsciiToString(FF), "[FF]") 302 data = strings.ReplaceAll(data, AsciiToString(CR), "[CR]") 303 data = strings.ReplaceAll(data, AsciiToString(SO), "[SO]") 304 data = strings.ReplaceAll(data, AsciiToString(SI), "[SI]") 305 data = strings.ReplaceAll(data, AsciiToString(SP), "[SP]") 306 data = strings.ReplaceAll(data, AsciiToString(SYN), "[SYN]") 307 data = strings.ReplaceAll(data, AsciiToString(CAN), "[CAN]") 308 data = strings.ReplaceAll(data, AsciiToString(EM), "[EM]") 309 data = strings.ReplaceAll(data, AsciiToString(SUB), "[SUB]") 310 data = strings.ReplaceAll(data, AsciiToString(ESC), "[ESC]") 311 312 return data 313 } 314 315 // ControlCharToASCII converts non-printable control char represented in word to ascii non-printable form 316 func ControlCharToASCII(data string) string { 317 data = strings.ReplaceAll(data, "[STX]", AsciiToString(STX)) 318 data = strings.ReplaceAll(data, "[ETX]", AsciiToString(ETX)) 319 data = strings.ReplaceAll(data, "[ETB]", AsciiToString(ETB)) 320 data = strings.ReplaceAll(data, "[ACK]", AsciiToString(ACK)) 321 data = strings.ReplaceAll(data, "[NAK]", AsciiToString(NAK)) 322 data = strings.ReplaceAll(data, "[ENQ]", AsciiToString(ENQ)) 323 data = strings.ReplaceAll(data, "[DLE]", AsciiToString(DLE)) 324 data = strings.ReplaceAll(data, "[DC1]", AsciiToString(DC1)) 325 data = strings.ReplaceAll(data, "[DC2]", AsciiToString(DC2)) 326 data = strings.ReplaceAll(data, "[DC3]", AsciiToString(DC3)) 327 data = strings.ReplaceAll(data, "[DC4]", AsciiToString(DC4)) 328 data = strings.ReplaceAll(data, "[FS]", AsciiToString(FS)) 329 data = strings.ReplaceAll(data, "[US]", AsciiToString(US)) 330 data = strings.ReplaceAll(data, "[GS]", AsciiToString(GS)) 331 data = strings.ReplaceAll(data, "[RS]", AsciiToString(RS)) 332 data = strings.ReplaceAll(data, "[BS]", AsciiToString(BS)) 333 data = strings.ReplaceAll(data, "[BEL]", AsciiToString(BEL)) 334 data = strings.ReplaceAll(data, "[DEL]", AsciiToString(DEL)) 335 data = strings.ReplaceAll(data, "[EOT]", AsciiToString(EOT)) 336 data = strings.ReplaceAll(data, "[COMMA]", AsciiToString(COMMA)) 337 data = strings.ReplaceAll(data, "[COLON]", AsciiToString(COLON)) 338 data = strings.ReplaceAll(data, "[PIPE]", AsciiToString(PIPE)) 339 data = strings.ReplaceAll(data, "[NULL]", AsciiToString(NUL)) 340 data = strings.ReplaceAll(data, "[SOH]", AsciiToString(SOH)) 341 data = strings.ReplaceAll(data, "[HT]", AsciiToString(HT)) 342 data = strings.ReplaceAll(data, "[LF]", AsciiToString(LF)) 343 data = strings.ReplaceAll(data, "[VT]", AsciiToString(VT)) 344 data = strings.ReplaceAll(data, "[FF]", AsciiToString(FF)) 345 data = strings.ReplaceAll(data, "[CR]", AsciiToString(CR)) 346 data = strings.ReplaceAll(data, "[SO]", AsciiToString(SO)) 347 data = strings.ReplaceAll(data, "[SI]", AsciiToString(SI)) 348 data = strings.ReplaceAll(data, "[SP]", AsciiToString(SP)) 349 data = strings.ReplaceAll(data, "[SYN]", AsciiToString(SYN)) 350 data = strings.ReplaceAll(data, "[CAN]", AsciiToString(CAN)) 351 data = strings.ReplaceAll(data, "[EM]", AsciiToString(EM)) 352 data = strings.ReplaceAll(data, "[SUB]", AsciiToString(SUB)) 353 data = strings.ReplaceAll(data, "[ESC]", AsciiToString(ESC)) 354 355 return data 356 } 357 358 // EscapeNonPrintable converts non printable \x00 - \x1f to pseudo escaped format 359 func EscapeNonPrintable(data string) string { 360 data = strings.Replace(data, AsciiToString(NUL), "[NUL_00]", -1) 361 data = strings.Replace(data, AsciiToString(SOH), "[SOH_01]", -1) 362 data = strings.Replace(data, AsciiToString(STX), "[STX_02]", -1) 363 data = strings.Replace(data, AsciiToString(ETX), "[ETX_03]", -1) 364 data = strings.Replace(data, AsciiToString(EOT), "[EOT_04]", -1) 365 data = strings.Replace(data, AsciiToString(ENQ), "[ENQ_05]", -1) 366 data = strings.Replace(data, AsciiToString(ACK), "[ACK_06]", -1) 367 data = strings.Replace(data, AsciiToString(BEL), "[BEL_07]", -1) 368 data = strings.Replace(data, AsciiToString(BS), "[BS_08]", -1) 369 data = strings.Replace(data, AsciiToString(HT), "[HT_09]", -1) 370 data = strings.Replace(data, AsciiToString(LF), "[LF_0A]", -1) 371 data = strings.Replace(data, AsciiToString(VT), "[VT_0B]", -1) 372 data = strings.Replace(data, AsciiToString(FF), "[FF_0C]", -1) 373 data = strings.Replace(data, AsciiToString(CR), "[CR_0D]", -1) 374 data = strings.Replace(data, AsciiToString(SO), "[SO_0E]", -1) 375 data = strings.Replace(data, AsciiToString(SI), "[SI_0F]", -1) 376 data = strings.Replace(data, AsciiToString(DLE), "[DLE_10]", -1) 377 data = strings.Replace(data, AsciiToString(DC1), "[DC1_11]", -1) 378 data = strings.Replace(data, AsciiToString(DC2), "[DC2_12]", -1) 379 data = strings.Replace(data, AsciiToString(DC3), "[DC3_13]", -1) 380 data = strings.Replace(data, AsciiToString(DC4), "[DC4_14]", -1) 381 data = strings.Replace(data, AsciiToString(NAK), "[NAK_15]", -1) 382 data = strings.Replace(data, AsciiToString(SYN), "[SYN_16]", -1) 383 data = strings.Replace(data, AsciiToString(ETB), "[ETB_17]", -1) 384 data = strings.Replace(data, AsciiToString(CAN), "[CAN_18]", -1) 385 data = strings.Replace(data, AsciiToString(EM), "[EM_19]", -1) 386 data = strings.Replace(data, AsciiToString(SUB), "[SUB_1A]", -1) 387 data = strings.Replace(data, AsciiToString(ESC), "[ESC_1B]", -1) 388 data = strings.Replace(data, AsciiToString(CR), "[CR_1C]", -1) 389 data = strings.Replace(data, AsciiToString(FS), "[FS_1D]", -1) 390 data = strings.Replace(data, AsciiToString(RS), "[RS_1E]", -1) 391 data = strings.Replace(data, AsciiToString(US), "[US_1F]", -1) 392 393 return data 394 } 395 396 // UnescapeNonPrintable converts pseudo escaped back to non printable form 397 func UnescapeNonPrintable(data string) string { 398 data = strings.Replace(data, "[NUL_00]", AsciiToString(NUL), -1) 399 data = strings.Replace(data, "[SOH_01]", AsciiToString(SOH), -1) 400 data = strings.Replace(data, "[STX_02]", AsciiToString(STX), -1) 401 data = strings.Replace(data, "[ETX_03]", AsciiToString(ETX), -1) 402 data = strings.Replace(data, "[EOT_04]", AsciiToString(EOT), -1) 403 data = strings.Replace(data, "[ENQ_05]", AsciiToString(ENQ), -1) 404 data = strings.Replace(data, "[ACK_06]", AsciiToString(ACK), -1) 405 data = strings.Replace(data, "[BEL_07]", AsciiToString(BEL), -1) 406 data = strings.Replace(data, "[BS_08]", AsciiToString(BS), -1) 407 data = strings.Replace(data, "[HT_09]", AsciiToString(HT), -1) 408 data = strings.Replace(data, "[LF_0A]", AsciiToString(LF), -1) 409 data = strings.Replace(data, "[VT_0B]", AsciiToString(VT), -1) 410 data = strings.Replace(data, "[FF_0C]", AsciiToString(FF), -1) 411 data = strings.Replace(data, "[CR_0D]", AsciiToString(CR), -1) 412 data = strings.Replace(data, "[SO_0E]", AsciiToString(SO), -1) 413 data = strings.Replace(data, "[SI_0F]", AsciiToString(SI), -1) 414 data = strings.Replace(data, "[DLE_10]", AsciiToString(DLE), -1) 415 data = strings.Replace(data, "[DC1_11]", AsciiToString(DC1), -1) 416 data = strings.Replace(data, "[DC2_12]", AsciiToString(DC2), -1) 417 data = strings.Replace(data, "[DC3_13]", AsciiToString(DC3), -1) 418 data = strings.Replace(data, "[DC4_14]", AsciiToString(DC4), -1) 419 data = strings.Replace(data, "[NAK_15]", AsciiToString(NAK), -1) 420 data = strings.Replace(data, "[SYN_16]", AsciiToString(SYN), -1) 421 data = strings.Replace(data, "[ETB_17]", AsciiToString(ETB), -1) 422 data = strings.Replace(data, "[CAN_18]", AsciiToString(CAN), -1) 423 data = strings.Replace(data, "[EM_19]", AsciiToString(EM), -1) 424 data = strings.Replace(data, "[SUB_1A]", AsciiToString(SUB), -1) 425 data = strings.Replace(data, "[ESC_1B]", AsciiToString(ESC), -1) 426 data = strings.Replace(data, "[CR_1C]", AsciiToString(CR), -1) 427 data = strings.Replace(data, "[FS_1D]", AsciiToString(FS), -1) 428 data = strings.Replace(data, "[RS_1E]", AsciiToString(RS), -1) 429 data = strings.Replace(data, "[US_1F]", AsciiToString(US), -1) 430 431 return data 432 }