1 21 22 package org.armedbear.j; 23 24 import java.io.BufferedOutputStream ; 25 import java.io.IOException ; 26 import java.io.InputStream ; 27 import java.io.UnsupportedEncodingException ; 28 import java.util.List ; 29 30 public class SystemBuffer implements Constants 32 { 33 public static final int TYPE_SYSTEM = 0; 34 public static final int TYPE_NORMAL = 1; 35 public static final int TYPE_ARCHIVE = 2; 36 public static final int TYPE_DIRECTORY = 3; 37 public static final int TYPE_SHELL = 4; 38 public static final int TYPE_MAN = 5; 39 public static final int TYPE_OUTPUT = 6; 40 public static final int TYPE_IMAGE = 7; 41 public static final int TYPE_MAILBOX = 8; 42 public static final int TYPE_TELNET = 9; 43 public static final int TYPE_SSH = 10; 44 public static final int TYPE_LIST_OCCURRENCES = 11; 45 46 protected int type = TYPE_SYSTEM; 47 protected boolean readOnly; 48 protected boolean forceReadOnly; 49 protected Mode mode; 50 protected String lineSeparator; 51 protected int lineCount; 52 53 private boolean isLoaded; 54 private Line firstLine; 55 private Line lastLine; 56 private File file; 57 private String loadEncoding; 58 private List tags; 59 60 public SystemBuffer() 61 { 62 } 63 64 public SystemBuffer(File file) 65 { 66 this.file = file; 67 } 68 69 public final int getType() 70 { 71 return type; 72 } 73 74 public final synchronized Line getFirstLine() 75 { 76 return firstLine; 77 } 78 79 public synchronized void setFirstLine(Line line) 80 { 81 firstLine = line; 82 } 83 84 public final Position getEnd() 85 { 86 Line line = firstLine; 87 if (line == null) 88 return null; 89 while (line.next() != null) 90 line = line.next(); 91 return new Position(line, line.length()); 92 } 93 94 public final File getFile() 95 { 96 return file; 97 } 98 99 public final void setFile(File file) 100 { 101 this.file = file; 102 } 103 104 public final synchronized boolean isLoaded() 105 { 106 return isLoaded; 107 } 108 109 public final synchronized void setLoaded(boolean b) 110 { 111 isLoaded = b; 112 } 113 114 public final Mode getMode() 115 { 116 return mode; 117 } 118 119 public final int getModeId() 120 { 121 return mode == null ? 0 : mode.getId(); 122 } 123 124 public final String getModeName() 125 { 126 return mode == null ? null : mode.toString(); 127 } 128 129 public synchronized final List getTags() 130 { 131 return tags; 132 } 133 134 public synchronized final void setTags(List tags) 135 { 136 this.tags = tags; 137 } 138 139 public final void setForceReadOnly(boolean b) 140 { 141 forceReadOnly = b; 142 } 143 144 public String getLineSeparator() 145 { 146 return lineSeparator; 147 } 148 149 public final boolean contains(Line line) 150 { 151 Line l = getFirstLine(); 152 while (l != null) { 153 if (l == line) 154 return true; 155 l = l.next(); 156 } 157 return false; 158 } 159 160 public int load() 161 { 162 if (!isLoaded) { 163 try { 164 if (file.isFile()) { 165 InputStream in = file.getInputStream(); 166 if (in != null) { 167 load(in, file.getEncoding()); 168 in.close(); 169 } 170 } 171 if (getFirstLine() == null) { 172 appendLine(""); 174 lineSeparator = System.getProperty("line.separator"); 175 } 176 } 177 catch (IOException e) { 178 Log.error(e); 179 } 180 isLoaded = true; 181 } 182 return LOAD_COMPLETED; 183 } 184 185 public void load(InputStream istream, String encoding) 186 { 187 if (mode != null && mode.getId() == BINARY_MODE) { 188 loadBinary(istream); 189 return; 190 } 191 byte[] buf = new byte[4096]; 192 int totalBytes = 0; 193 try { 194 int bytesRead = istream.read(buf); 195 loadProgress(totalBytes = totalBytes + bytesRead); 196 boolean isUnicode = false; 198 boolean isLittleEndian = true; 199 if (bytesRead >= 2) { 200 byte byte1 = buf[0]; 201 byte byte2 = buf[1]; 202 if (byte1 == (byte) 0xfe && byte2 == (byte) 0xff) { 203 isUnicode = true; 204 isLittleEndian = false; 205 loadEncoding = "UnicodeBig"; 206 } else if (byte1 == (byte) 0xff && byte2 == (byte) 0xfe) { 207 isUnicode = true; 208 loadEncoding = "UnicodeLittle"; 209 } 210 } 211 boolean skipLF = false; 212 if (isUnicode) { 213 FastStringBuffer sb = new FastStringBuffer(256); 214 int i = 2; 215 while (bytesRead > 0) { 216 while (i < bytesRead - 1) { 217 char c; 218 final byte b1 = buf[i++]; 219 final byte b2 = buf[i++]; 220 if (isLittleEndian) 221 c = (char) ((b2 << 8) + (b1 & 0xff)); 222 else 223 c = (char) ((b1 << 8) + (b2 & 0xff)); 224 switch (c) { 225 case '\r': 226 appendLine(sb.toString()); 227 sb.setLength(0); 228 skipLF = true; 229 break; 230 case '\n': 231 if (skipLF) { 232 if (lineSeparator == null) 234 lineSeparator = "\r\n"; 235 skipLF = false; 236 } else { 237 if (lineSeparator == null) 239 lineSeparator = "\n"; 240 appendLine(sb.toString()); 241 sb.setLength(0); 242 } 243 break; 244 default: 245 if (skipLF) { 247 if (lineSeparator == null) 249 lineSeparator = "\r"; 250 skipLF = false; 251 } 252 sb.append(c); 253 break; 254 } 255 } 256 bytesRead = istream.read(buf); 257 i = 0; 258 } 259 if (sb.length() > 0) { 260 appendLine(sb.toString()); 262 } else { 263 appendLine(""); 267 } 268 } else { 269 if (encoding == null) { 271 encoding = 272 Editor.preferences().getStringProperty(Property.DEFAULT_ENCODING); 273 } 274 loadEncoding = encoding; 275 ByteBuffer bb = new ByteBuffer(256); 276 while (bytesRead > 0) { 277 for (int i = 0; i < bytesRead; i++) { 278 byte b = buf[i]; 279 switch (b) { 280 case 13: 281 appendLine(new String (bb.getBytes(), 0, bb.length(), encoding)); 282 bb.setLength(0); 283 skipLF = true; 284 break; 285 case 10: 286 if (skipLF) { 287 if (lineSeparator == null) 289 lineSeparator = "\r\n"; 290 skipLF = false; 291 } else { 292 if (lineSeparator == null) 294 lineSeparator = "\n"; 295 appendLine(new String (bb.getBytes(), 0, bb.length(), encoding)); 296 bb.setLength(0); 297 } 298 break; 299 default: 300 if (skipLF) { 302 if (lineSeparator == null) 304 lineSeparator = "\r"; 305 skipLF = false; 306 } 307 bb.append(b); 308 break; 309 } 310 } 311 bytesRead = istream.read(buf); 312 if (bytesRead > 0) 313 loadProgress(totalBytes = totalBytes + bytesRead); 314 } 315 if (bb.length() > 0) { 316 appendLine(new String (bb.getBytes(), 0, bb.length(), encoding)); 318 } else { 319 appendLine(""); 323 } 324 } 325 isLoaded = true; 326 } 327 catch (Exception e) { 328 Log.error(e); 329 } 330 loadFinished(isLoaded); 331 } 332 333 public final Line getLastLine() 334 { 335 return lastLine; 336 } 337 338 public final void setLastLine(Line line) 339 { 340 lastLine = line; 341 } 342 343 protected void appendLine(Line line) 344 { 345 line.setPrevious(lastLine); 346 if (lastLine != null) 347 lastLine.setNext(line); 348 lastLine = line; 349 if (getFirstLine() == null) 350 setFirstLine(line); 351 } 352 353 public void appendLine(String s) 354 { 355 appendLine(new TextLine(s)); 356 } 357 358 public void append(String s) 359 { 360 int begin = 0; 361 int end = 0; 362 boolean skipLF = false; 363 final int limit = s.length(); 364 for (int i = 0; i < limit; i++) { 365 switch (s.charAt(i)) { 366 case '\r': 367 appendLine(s.substring(begin, end)); 368 ++end; 369 begin = end; 370 skipLF = true; 371 break; 372 case '\n': 373 if (skipLF) { 374 ++begin; 375 ++end; 376 skipLF = false; 377 } else { 378 appendLine(s.substring(begin, end)); 379 ++end; 380 begin = end; 381 } 382 break; 383 default: 384 skipLF = false; 385 ++end; 386 } 387 } 388 if (begin < end) 389 appendLine(s.substring(begin, end)); 390 } 391 392 private void appendBinaryLine(int start, byte[] bytes, int numBytes) 393 { 394 appendLine(new BinaryLine(start, bytes, numBytes)); 395 } 396 397 public void renumber() 399 { 400 for (Line line = getFirstLine(); line != null; line = line.next()) 401 line.setLineNumber(lineCount++); 402 } 403 404 public void writeBuffer() throws SaveException 405 { 406 if (file.isFile() && !file.canWrite()) { 407 Log.error("writeFile: file is not writable: " + file); 408 throw new SaveException(file, 409 file.canonicalPath() + " is not writable"); 410 } 411 if (Platform.isPlatformWindows()) { 412 File tempFile = writeTemporaryFile(); 414 if (!makePatchFile()) { 415 if (!Utilities.makeBackup(file, false)) { 416 Log.error("backup failed"); 417 throw new SaveException(file, 418 "Unable to write backup file for " + file.canonicalPath()); 419 } 420 } 421 if (!Utilities.deleteRename(tempFile, file)) { 422 Log.error("unable to rename " + tempFile.canonicalPath() + 423 " to " + file.canonicalPath()); 424 throw new SaveException(file, 425 "Unable to rename temporary file"); 426 } 427 } else { 428 if (!makePatchFile()) { 431 if (!Utilities.makeBackup(file, true)) { 432 Log.error("backup failed"); 433 throw new SaveException(file, 434 "Unable to write backup file for ".concat( 435 file.canonicalPath())); 436 } 437 } 438 if (!writeFile(file)) { 440 Log.error("writeFile failed"); 441 throw new SaveException(file, 442 "Unable to write ".concat(file.canonicalPath())); 443 } 444 } 445 } 446 447 private final boolean makePatchFile() 449 { 450 if (file.isFile()) { 451 File patchFile = getPatchFile(); 452 if (patchFile != null) { 453 if (!patchFile.isFile()) 454 return Utilities.copyFile(file, patchFile); 455 } 456 } 457 return false; 458 } 459 460 public final File getPatchFile() 463 { 464 String suffix; 465 if (this instanceof Buffer) 466 suffix = ((Buffer)this).getStringProperty(Property.PATCH_MODE); 467 else if (mode != null) 468 suffix = mode.getStringProperty(Property.PATCH_MODE); 469 else { 470 suffix = 471 Editor.preferences().getStringProperty(Property.PATCH_MODE); 472 } 473 if (suffix != null) { 474 suffix = suffix.trim(); 475 if (suffix.length() > 0) { 476 if (suffix.charAt(0) != '.') 477 suffix = ".".concat(suffix); 478 return File.getInstance(file.canonicalPath().concat(suffix)); 479 } 480 } 481 return null; 482 } 483 484 public boolean writeFile(File outputFile) 485 { 486 try { 487 BufferedOutputStream out = new BufferedOutputStream (outputFile.getOutputStream()); 488 if (lineSeparator == null) 489 lineSeparator = System.getProperty("line.separator"); 490 String encoding = outputFile.getEncoding(); 491 if (encoding == null) 492 encoding = getSaveEncoding(); 493 Line line = getFirstLine(); 494 if (line != null) { 495 final byte[] byteOrderMark = getByteOrderMark(encoding); 496 if (byteOrderMark != null) 497 out.write(byteOrderMark); 498 out.write(line.getBytes(encoding)); 499 line = line.next(); 500 final byte[] sepBytes = getSeparatorBytes(encoding); 501 while (line != null) { 502 out.write(sepBytes); 503 out.write(line.getBytes(encoding)); 504 line = line.next(); 505 } 506 } 507 out.flush(); 508 out.close(); 509 return true; 510 } 511 catch (IOException e) { 512 Log.error(e); 513 return false; 514 } 515 } 516 517 public String getSaveEncoding() 518 { 519 String encoding = file == null ? null : file.getEncoding(); 520 if (encoding == null) { 521 encoding = loadEncoding; 522 if (encoding == null) 523 encoding = Editor.preferences().getStringProperty( 524 Property.DEFAULT_ENCODING); 525 } 526 if (encoding == null) 527 Debug.bug(); 528 return encoding; 529 } 530 531 byte[] getByteOrderMark(String encoding) throws UnsupportedEncodingException 532 { 533 byte[] bytes = "test".getBytes(encoding); 534 if ((bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) || 535 (bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) { 536 byte[] byteOrderMark = new byte[2]; 537 byteOrderMark[0] = bytes[0]; 538 byteOrderMark[1] = bytes[1]; 539 return byteOrderMark; 540 } 541 return null; 542 } 543 544 byte[] getSeparatorBytes(String encoding) throws UnsupportedEncodingException 545 { 546 byte[] bytes = lineSeparator.getBytes(encoding); 547 if (bytes.length > 2) { 548 if ((bytes[0] == (byte) 0xfe && bytes[1] == (byte) 0xff) || 549 (bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xfe)) { 550 byte[] sepBytes = new byte[bytes.length-2]; 551 for (int i = 0; i < sepBytes.length; i++) 552 sepBytes[i] = bytes[i+2]; 553 return sepBytes; 554 } 555 } 556 return bytes; 557 } 558 559 synchronized void _empty() 560 { 561 Line line = getFirstLine(); 562 while (line != null) { 563 Line nextLine = line.next(); 564 line.setPrevious(null); 565 line.setNext(null); 566 line = nextLine; 567 } 568 setFirstLine(null); 569 lastLine = null; 570 isLoaded = false; 571 } 572 573 protected void loadProgress(int totalBytesRead) 574 { 575 } 577 578 protected void loadFinished(boolean success) 579 { 580 } 582 583 private void loadBinary(InputStream istream) 584 { 585 byte[] array = readAllBytes(istream); 586 if (array != null) { 587 for (int start = 0; start < array.length; start += 16) { 588 int bytesLeft = array.length - start; 589 int count = bytesLeft >= 16 ? 16 : bytesLeft; 590 appendBinaryLine(start, array, count); 591 } 592 isLoaded = true; 593 } 594 loadFinished(isLoaded); 595 } 596 597 private byte[] readAllBytes(InputStream in) 598 { 599 final int chunkSize = 0x8000; 600 byte[] array = null; 601 int totalBytes = 0; 602 byte[] chunk = new byte[ chunkSize ]; 603 int bytesRead; 604 try { 605 while ((bytesRead = in.read(chunk, 0, chunk.length)) > 0) { 606 if (array == null) { 607 array = new byte[bytesRead]; 608 System.arraycopy(chunk, 0, array, 0, bytesRead); 609 } else { 610 byte[] newArray = new byte[totalBytes + bytesRead]; 612 613 if (totalBytes > 0) 615 System.arraycopy(array, 0, newArray, 0, totalBytes); 616 617 System.arraycopy(chunk, 0, newArray, totalBytes, bytesRead); 619 620 array = newArray; 621 } 622 totalBytes += bytesRead; 623 Debug.assertTrue(array.length == totalBytes); 624 } 625 } 626 catch (IOException e) { 627 Log.error(e); 628 array = null; 629 } 630 return array; 631 } 632 633 private File writeTemporaryFile() throws SaveException 634 { 635 boolean succeeded = false; 636 File tempFile = Utilities.getTempFile(file.getParent()); 638 if (tempFile != null) 639 succeeded = writeFile(tempFile); 640 if (!succeeded) { 641 tempFile = Utilities.getTempFile(); 645 if (tempFile != null) 646 succeeded = writeFile(tempFile); 647 } 648 if (!succeeded) { 649 throw new SaveException(file, 650 "Unable to write temporary file for ".concat( 651 file.canonicalPath())); 652 } 653 return tempFile; 654 } 655 } 656 | Popular Tags |