1 18 19 package com.Ostermiller.util; 20 21 import java.io.*; 22 import gnu.getopt.*; 23 import java.text.MessageFormat ; 24 import java.util.ResourceBundle ; 25 import java.util.Locale ; 26 27 36 public class Tabs { 37 38 43 public static final String version = "1.0"; 44 45 50 protected static ResourceBundle labels = ResourceBundle.getBundle("com.Ostermiller.util.Tabs", Locale.getDefault()); 51 52 53 58 public final static int TABS = -1; 59 60 68 public static void main(String [] args){ 69 LongOpt[] longopts = { 71 new LongOpt(labels.getString("help.option"), LongOpt.NO_ARGUMENT, null, 1), 72 new LongOpt(labels.getString("version.option"), LongOpt.NO_ARGUMENT, null, 2), 73 new LongOpt(labels.getString("about.option"), LongOpt.NO_ARGUMENT, null, 3), 74 new LongOpt(labels.getString("width.option"), LongOpt.REQUIRED_ARGUMENT, null, 'w'), 75 new LongOpt(labels.getString("guess.option"), LongOpt.NO_ARGUMENT, null, 'g'), 76 new LongOpt(labels.getString("tabs.option"), LongOpt.NO_ARGUMENT, null, 't'), 77 new LongOpt(labels.getString("spaces.option"), LongOpt.REQUIRED_ARGUMENT, null, 's'), 78 new LongOpt(labels.getString("force.option"), LongOpt.NO_ARGUMENT, null, 'f'), 79 new LongOpt(labels.getString("quiet.option"), LongOpt.NO_ARGUMENT, null, 'q'), 80 new LongOpt(labels.getString("reallyquiet.option"), LongOpt.NO_ARGUMENT, null, 'Q'), 81 new LongOpt(labels.getString("verbose.option"), LongOpt.NO_ARGUMENT, null, 'v'), 82 new LongOpt(labels.getString("reallyverbose.option"), LongOpt.NO_ARGUMENT, null, 'V'), 83 new LongOpt(labels.getString("noforce.option"), LongOpt.NO_ARGUMENT, null, 4), 84 }; 85 String oneLetterOptions = "w:gts:fVvqQ"; 86 Getopt opts = new Getopt(labels.getString("tabs"), args, oneLetterOptions, longopts); 87 int inputTabWidth = TABS; 88 int outputTabWidth = 4; 89 boolean force = false; 90 boolean printMessages = false; 91 boolean printExtraMessages = false; 92 boolean printErrors = true; 93 int c; 94 while ((c = opts.getopt()) != -1){ 95 switch(c){ 96 case 1:{ 97 String [] helpFlags = new String []{ 99 "--" + labels.getString("help.option"), 100 "--" + labels.getString("version.option"), 101 "--" + labels.getString("about.option"), 102 "-w --" + labels.getString("width.option") + " <" + labels.getString("s.arg") + ">", 103 "-g --" + labels.getString("guess.option"), 104 "-t --" + labels.getString("tabs.option"), 105 "-s --" + labels.getString("spaces.option") + " <" + labels.getString("s.arg") + ">", 106 "-f --" + labels.getString("force.option"), 107 "--" + labels.getString("noforce.option"), 108 "-V --" + labels.getString("reallyverbose.option"), 109 "-v --" + labels.getString("verbose.option"), 110 "-q --" + labels.getString("quiet.option"), 111 "-Q --" + labels.getString("reallyquiet.option"), 112 }; 113 int maxLength = 0; 114 for (int i=0; i<helpFlags.length; i++){ 115 maxLength = Math.max(maxLength, helpFlags[i].length()); 116 } 117 maxLength += 2; 118 System.out.println( 119 labels.getString("tabs") + " [-" + StringHelper.replace(oneLetterOptions,":","") + "] <" + labels.getString("files") + ">" + "\n" + 120 labels.getString("purpose.message") + "\n" + 121 " " + labels.getString("stdin.message") + "\n" + 122 " " + StringHelper.postpad(helpFlags[0] ,maxLength, ' ') + labels.getString("help.message") + "\n" + 123 " " + StringHelper.postpad(helpFlags[1] ,maxLength, ' ') + labels.getString("version.message") + "\n" + 124 " " + StringHelper.postpad(helpFlags[2] ,maxLength, ' ') + labels.getString("about.message") + "\n" + 125 " " + StringHelper.postpad(helpFlags[3] ,maxLength, ' ') + labels.getString("w.message") + "\n" + 126 " " + StringHelper.postpad(helpFlags[4] ,maxLength, ' ') + labels.getString("g.message") + " (" + labels.getString("default") + ")\n" + 127 " " + StringHelper.postpad(helpFlags[5] ,maxLength, ' ') + labels.getString("t.message") + "\n" + 128 " " + StringHelper.postpad(helpFlags[6] ,maxLength, ' ') + labels.getString("s.message") + " (" + labels.getString("default") + "=4)\n" + 129 " " + StringHelper.postpad(helpFlags[7] ,maxLength, ' ') + labels.getString("f.message") + "\n" + 130 " " + StringHelper.postpad(helpFlags[8] ,maxLength, ' ') + labels.getString("noforce.message") + " (" + labels.getString("default") + ")\n" + 131 " " + StringHelper.postpad(helpFlags[9] ,maxLength, ' ') + labels.getString("V.message") + "\n" + 132 " " + StringHelper.postpad(helpFlags[10] ,maxLength, ' ') + labels.getString("v.message") + "\n" + 133 " " + StringHelper.postpad(helpFlags[11] ,maxLength, ' ') + labels.getString("q.message") + " (" + labels.getString("default") + ")\n" + 134 " " + StringHelper.postpad(helpFlags[12] ,maxLength, ' ') + labels.getString("Q.message") + "\n" 135 ); 136 System.exit(0); 137 } break; 138 case 2:{ 139 System.out.println(MessageFormat.format(labels.getString("version"), (Object [])new String [] {version})); 141 System.exit(0); 142 } break; 143 case 3:{ 144 System.out.println( 145 labels.getString("tabs") + " -- " + labels.getString("purpose.message") + "\n" + 146 MessageFormat.format(labels.getString("copyright"), (Object [])new String [] {"2002", "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)"}) + "\n\n" + 147 labels.getString("license") 148 ); 149 System.exit(0); 150 } break; 151 case 'w':{ 152 try { 153 inputTabWidth = Integer.parseInt(opts.getOptarg()); 154 } catch (NumberFormatException x){ 155 inputTabWidth = -1; 156 } 157 if (inputTabWidth<1 || inputTabWidth>20){ 158 System.err.println(labels.getString("widtherror")); 159 System.exit(1); 160 } 161 } break; 162 case 'g':{ 163 inputTabWidth = TABS; 164 } break; 165 case 's':{ 166 try { 167 outputTabWidth = Integer.parseInt(opts.getOptarg()); 168 } catch (NumberFormatException x){ 169 outputTabWidth = -1; 170 } 171 if (outputTabWidth<1 || outputTabWidth>20){ 172 System.err.println("widtherror"); 173 System.exit(1); 174 } 175 } break; 176 case 't':{ 177 outputTabWidth = TABS; 178 } break; 179 case 'f':{ 180 force = true; 181 } break; 182 case 4:{ 183 force = false; 184 } break; 185 case 'V':{ 186 printExtraMessages = true; 187 printMessages = true; 188 printErrors = true; 189 } break; 190 case 'v':{ 191 printExtraMessages = false; 192 printMessages = true; 193 printErrors = true; 194 } break; 195 case 'q':{ 196 printExtraMessages = false; 197 printMessages = false; 198 printErrors = true; 199 } break; 200 case 'Q':{ 201 printExtraMessages = false; 202 printMessages = false; 203 printErrors = false; 204 } break; 205 default:{ 206 System.exit(1); 207 } 208 } 209 } 210 211 int exitCond = 0; 212 boolean done = false; 213 for (int i=opts.getOptind(); i<args.length; i++){ 214 boolean modified = false; 215 done = true; 216 File source = new File(args[i]); 217 if (!source.exists()){ 218 if(printErrors){ 219 System.err.println(MessageFormat.format(labels.getString("doesnotexist"), (Object [])new String [] {args[i]})); 220 } 221 exitCond = 1; 222 } else if (!source.canRead()){ 223 if(printErrors){ 224 System.err.println(MessageFormat.format(labels.getString("cantread"), (Object [])new String [] {args[i]})); 225 } 226 exitCond = 1; 227 } else if (!source.canWrite()){ 228 if(printErrors){ 229 System.err.println(MessageFormat.format(labels.getString("cantwrite"), (Object [])new String [] {args[i]})); 230 } 231 exitCond = 1; 232 } else { 233 try { 234 if(convert (source, inputTabWidth, outputTabWidth, !force)){ 235 if (printMessages){ 236 System.out.println(MessageFormat.format(labels.getString("modified"), (Object [])new String [] {args[i]})); 237 } 238 } else { 239 if (printExtraMessages){ 240 System.out.println(MessageFormat.format(labels.getString("alreadycorrect"), (Object [])new String [] {args[i]})); 241 } 242 } 243 } catch (IOException x){ 244 if(printErrors){ 245 System.err.println(args[i] + ": " + x.getMessage()); 246 } 247 exitCond = 1; 248 } 249 } 250 } 251 if (!done){ 252 if(inputTabWidth == TABS){ 253 System.err.println(labels.getString("stdinguess")); 254 exitCond = 1; 255 } else { 256 try { 257 convert (System.in, System.out, inputTabWidth, outputTabWidth, !force); 258 } catch (IOException x){ 259 System.err.println(x.getMessage()); 260 exitCond = 1; 261 } 262 } 263 } 264 System.exit(exitCond); 265 } 266 267 private final static int DEFAULT_INPUT_TAB_WIDTH = 4; 268 private final static int DEFAULT_INPUT_FILE_TAB_WIDTH = TABS; 269 private final static int DEFAULT_OUTPUT_TAB_WIDTH = 4; 270 271 private final static boolean DEFAULT_MODIFY_BINARY = false; 272 273 285 public static boolean convert(InputStream in, OutputStream out) throws IOException { 286 return convert(in, out, DEFAULT_INPUT_TAB_WIDTH, DEFAULT_OUTPUT_TAB_WIDTH, DEFAULT_MODIFY_BINARY); 287 } 288 289 303 public static boolean convert(InputStream in, OutputStream out, int inputTabWidth) throws IOException { 304 return convert(in, out, inputTabWidth, DEFAULT_OUTPUT_TAB_WIDTH, DEFAULT_MODIFY_BINARY); 305 } 306 307 324 public static boolean convert(InputStream in, OutputStream out, int inputTabWidth, int outputTabWidth) throws IOException { 325 return convert(in, out, inputTabWidth, outputTabWidth, DEFAULT_MODIFY_BINARY); 326 } 327 328 346 public static boolean convert(InputStream in, OutputStream out, int inputTabWidth, int outputTabWidth, boolean binaryException) throws IOException { 347 if ((inputTabWidth < 1 || inputTabWidth > 20) && inputTabWidth != TABS){ 348 throw new IllegalArgumentException (labels.getString("widtherror")); 349 } 350 if ((outputTabWidth < 1 || outputTabWidth > 20) && outputTabWidth != TABS){ 351 throw new IllegalArgumentException (labels.getString("widtherror")); 352 } 353 int state = STATE_INIT; 354 int spaces = 0; 355 int tabs = 0; 356 int tabStops = 0; 357 int extraSpaces = 0; 358 boolean modified = false; 359 360 byte[] buffer = new byte[BUFFER_SIZE]; 361 int read; 362 while((read = in.read(buffer)) != -1){ 363 for (int i=0; i<read; i++){ 364 byte b = buffer[i]; 365 if(binaryException && b!='\r' && b!='\n' && b!='\t' && b!='\f' && (b & 0xff)<32){ 366 throw new BinaryDataException(labels.getString("binaryexcepion")); 367 } 368 switch (b){ 369 case ' ': { 370 if (state == STATE_INIT) { 371 spaces++; 372 extraSpaces++; 373 if (extraSpaces == inputTabWidth){ 374 tabStops++; 375 extraSpaces = 0; 376 } 377 } else { 378 out.write(b); 379 } 380 } break; 381 case '\t': { 382 if (state == STATE_INIT) { 383 if (spaces > 0){ 384 modified = true; 386 } 387 tabs++; 388 tabStops++; 389 extraSpaces = 0; 390 } else { 391 out.write(b); 392 } 393 } break; 394 case '\r': case '\n': { 395 out.write(b); 396 spaces = 0; 397 tabs = 0; 398 tabStops = 0; 399 extraSpaces = 0; 400 state = STATE_INIT; 401 } break; 402 default: { 403 if (state == STATE_INIT){ 404 if (outputTabWidth == TABS){ 405 for (int j=0; j<tabStops; j++){ 406 out.write((byte)'\t'); 407 } 408 } else { 409 extraSpaces += tabStops * outputTabWidth; 410 tabStops = 0; 411 } 412 for (int j=0; j<extraSpaces; j++){ 413 out.write((byte)' '); 414 } 415 if (extraSpaces != spaces || tabStops != tabs) modified = true; 416 } 417 out.write(b); 418 state = STATE_SOMETHING; 419 } break; 420 } 421 } 422 } 423 return modified; 424 } 425 426 437 public static boolean convert(File f) throws IOException { 438 return convert(f, DEFAULT_INPUT_FILE_TAB_WIDTH, DEFAULT_OUTPUT_TAB_WIDTH, DEFAULT_MODIFY_BINARY); 439 } 440 441 454 public static boolean convert(File f, int inputTabWidth) throws IOException { 455 return convert(f, inputTabWidth, DEFAULT_OUTPUT_TAB_WIDTH, DEFAULT_MODIFY_BINARY); 456 } 457 458 471 public static boolean convert(File f, int inputTabWidth, int outputTabWidth) throws IOException { 472 return convert(f, inputTabWidth, outputTabWidth, DEFAULT_MODIFY_BINARY); 473 } 474 475 490 public static boolean convert(File f, int inputTabWidth, int outputTabWidth, boolean binaryException) throws IOException { 491 File temp = null; 492 InputStream in = null; 493 OutputStream out = null; 494 boolean modified = false; 495 try { 496 if (inputTabWidth == TABS){ 497 inputTabWidth = guessTabWidth(new FileInputStream(f)); 498 } 499 in = new FileInputStream(f); 500 temp = File.createTempFile("LineEnds", null, null); 501 out = new FileOutputStream(temp); 502 modified = convert(in, out, inputTabWidth, outputTabWidth, binaryException); 503 in.close(); 504 in = null; 505 out.flush(); 506 out.close(); 507 out = null; 508 if (modified){ 509 FileHelper.move(temp, f, true); 510 } else { 511 if (!temp.delete()){ 512 throw new IOException( 513 MessageFormat.format( 514 labels.getString("tempdeleteerror"), 515 (Object [])new String [] {temp.toString()} 516 ) 517 ); 518 } 519 } 520 } finally { 521 if (in != null){ 522 in.close(); 523 in = null; 524 } 525 if (out != null){ 526 out.flush(); 527 out.close(); 528 out = null; 529 } 530 } 531 return modified; 532 } 533 534 539 private final static int BUFFER_SIZE = 1024; 540 private final static int STATE_INIT = 0; 541 private final static int STATE_SOMETHING = 1; 542 543 final private static int MAX_SPACES = 128; 544 final private static int MAX_TABS = 16; 545 final private static int MAX_COMBINED = 256; 546 553 public static int guessTabWidth(InputStream in) throws IOException { 554 byte[] buffer = new byte[BUFFER_SIZE]; 555 int[][] data = new int[MAX_SPACES][MAX_TABS]; 556 int[] spaceData = new int[MAX_SPACES*MAX_TABS]; 557 int read; 558 int state = STATE_INIT; 559 int tabs = 0; 560 int spaces = 0; 561 int mostTabs = 0; 562 int mostSpaces = 0; 563 boolean spaceUsed = false; 564 while((read = in.read(buffer)) != -1){ 565 for (int i=0; i<read; i++){ 566 byte b = buffer[i]; 567 switch (b){ 568 case ' ': { 569 if (state == STATE_INIT) spaces++; 570 } break; 571 case '\t': { 572 if (state == STATE_INIT) tabs++; 573 } break; 574 case '\r': case '\n': { 575 state = STATE_INIT; 576 if (spaces < MAX_SPACES && tabs < MAX_TABS){ 577 data[spaces][tabs]++; 578 if (tabs > mostTabs) mostTabs = tabs; 579 if (spaces > mostSpaces) mostSpaces = spaces; 580 spaces = 0; 581 tabs = 0; 582 } 583 } break; 584 default: { 585 state = STATE_SOMETHING; 586 } break; 587 } 588 } 589 } 590 for (int tabWidth=2; tabWidth<=20; tabWidth++){ 591 int mostCombined=0; 592 for (int tabInd=0; tabInd <= mostTabs; tabInd++){ 593 for (int spaceInd=0; spaceInd <= mostSpaces; spaceInd++){ 594 int totInd = spaceInd + (tabInd * tabWidth); 595 if (totInd < MAX_COMBINED){ 596 int numLines = data[spaceInd][tabInd]; 597 if (numLines > 0){ 598 if (mostCombined < totInd) mostCombined = totInd; 599 spaceData[totInd] += numLines; 600 } 601 } 602 } 603 } 604 boolean found = true; 605 for(int combInd=0; found && combInd < mostCombined; combInd+=tabWidth){ 606 found = spaceData[combInd] > 0; 607 } 608 if (found) return tabWidth; 609 } 610 return 2; 611 } 612 } 613 | Popular Tags |