(rs *recordState)
| 292 | } |
| 293 | |
| 294 | func (csvr *csvReader) parseQuotedField(rs *recordState) (kontinue bool, err error) { |
| 295 | const quoteLen = len(`"`) |
| 296 | dl := len(csvr.delim) |
| 297 | recordStartLine := csvr.numLine |
| 298 | // full copy needed here because we append rs.line to fullField, and this can result in buffer corruption in |
| 299 | // some cases (namely when windows line endings are present) |
| 300 | fullField := make([]byte, len(rs.line)) |
| 301 | copy(fullField, rs.line) |
| 302 | |
| 303 | // Quoted string field |
| 304 | rs.line = rs.line[quoteLen:] |
| 305 | for { |
| 306 | i := bytes.IndexByte(rs.line, '"') |
| 307 | if i >= 0 { |
| 308 | // Hit next quote. |
| 309 | rs.recordBuffer = append(rs.recordBuffer, rs.line[:i]...) |
| 310 | rs.line = rs.line[i+quoteLen:] |
| 311 | |
| 312 | atDelimiter := len(rs.line) >= dl && bytes.Equal(rs.line[:dl], csvr.delim) |
| 313 | nextRune, _ := utf8.DecodeRune(rs.line) |
| 314 | |
| 315 | switch { |
| 316 | case atDelimiter: |
| 317 | // `"<delimiter>` sequence (end of field). |
| 318 | rs.line = rs.line[dl:] |
| 319 | rs.fieldIndexes = append(rs.fieldIndexes, len(rs.recordBuffer)) |
| 320 | return true, err |
| 321 | case nextRune == '"': |
| 322 | // `""` sequence (append quote). |
| 323 | rs.recordBuffer = append(rs.recordBuffer, '"') |
| 324 | rs.line = rs.line[quoteLen:] |
| 325 | case lengthNL(rs.line) == len(rs.line): |
| 326 | // `"\n` sequence (end of line). |
| 327 | rs.fieldIndexes = append(rs.fieldIndexes, len(rs.recordBuffer)) |
| 328 | return false, err |
| 329 | default: |
| 330 | // `"*` sequence (invalid non-escaped quote). |
| 331 | col := utf8.RuneCount(fullField[:len(fullField)-len(rs.line)-quoteLen]) |
| 332 | err = &csv.ParseError{StartLine: recordStartLine, Line: csvr.numLine, Column: col, Err: csv.ErrQuote} |
| 333 | return false, err |
| 334 | } |
| 335 | } else if len(rs.line) > 0 { |
| 336 | // Hit end of line (copy all data so far). |
| 337 | rs.recordBuffer = append(rs.recordBuffer, rs.line...) |
| 338 | if err != nil { |
| 339 | return false, err |
| 340 | } |
| 341 | |
| 342 | rs.line, err = csvr.readLine() |
| 343 | rs.rawData = append(rs.rawData, rs.line...) |
| 344 | if err == io.EOF { |
| 345 | err = nil |
| 346 | } |
| 347 | // If we get a partialLineError, populate the partialLine field with the full record data |
| 348 | // since quoted fields can span multiple lines, otherwise we wouldn't capture the initial |
| 349 | // lines of this record. |
| 350 | if ple, ok := err.(*partialLineError); ok { |
| 351 | ple.partialLine = string(rs.rawData) |
no test coverage detected