Index: src/uk/me/parabola/imgfmt/app/BufferedImgFileWriter.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/BufferedImgFileWriter.java	(revision 3258)
+++ src/uk/me/parabola/imgfmt/app/BufferedImgFileWriter.java	(working copy)
@@ -195,8 +195,8 @@
 				// Previous message was confusing people, although it is difficult to come
 				// up with something that is strictly true in all situations.
 				throw new MapFailedException(
-						"There is not enough room in a single garmin map for all the input data\n" +
-								"   The .osm file should be split into smaller pieces first.");
+						"There is not enough room in a single garmin map for all the input data." +
+								" The .osm file should be split into smaller pieces first.");
 			}
 			ByteBuffer newb = ByteBuffer.allocate(bufferSize);
 			newb.order(ByteOrder.LITTLE_ENDIAN);
Index: src/uk/me/parabola/imgfmt/MapFailedException.java
===================================================================
--- src/uk/me/parabola/imgfmt/MapFailedException.java	(revision 3258)
+++ src/uk/me/parabola/imgfmt/MapFailedException.java	(working copy)
@@ -12,6 +12,8 @@
  */
 package uk.me.parabola.imgfmt;
 
+import uk.me.parabola.log.Logger;
+
 /**
  * Used for cases where the current map has failed to compile, but the error
  * is expected to be specific to the map (eg it is too big etc).  When this
@@ -24,8 +26,8 @@
  * @author Steve Ratcliffe
  */
 public class MapFailedException extends RuntimeException {
+	private static final Logger log = Logger.getLogger(MapFailedException.class);
 
-
 	/**
 	 * Constructs a new runtime exception with the specified detail message.
 	 * The cause is not initialized, and may subsequently be initialized by a
@@ -36,6 +38,7 @@
 	 */
 	public MapFailedException(String message) {
 		super(message);
+		log(message);
 	}
 
 	/**
@@ -54,5 +57,17 @@
 	 */
 	public MapFailedException(String message, Throwable cause) {
 		super(message, cause);
+		log(message);
 	}
+	
+	private static void log(String message){
+		String thrownBy = "";
+		try{
+			StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+			int callerPosInStack = 3; 
+			String[] caller = stackTraceElements[callerPosInStack].getClassName().split("\\.");
+			thrownBy = "(thrown in " + caller[caller.length-1]+ "." +stackTraceElements[callerPosInStack].getMethodName() + "()) ";
+		} catch(Exception e){}
+		log.error(thrownBy + message);
+	}
 }
\ No newline at end of file
Index: src/uk/me/parabola/mkgmap/main/Main.java
===================================================================
--- src/uk/me/parabola/mkgmap/main/Main.java	(revision 3258)
+++ src/uk/me/parabola/mkgmap/main/Main.java	(working copy)
@@ -91,6 +91,7 @@
 	// used for messages in listStyles and checkStyles
 	private String searchedStyleName;
 
+	private int programRC = 0;
 	/**
 	 * The main program to make or combine maps.  We now use a two pass process,
 	 * first going through the arguments and make any maps and collect names
@@ -110,6 +111,7 @@
 
 		Main mm = new Main();
 
+		int numExitExceptions = 0;
 		try {
 			// Read the command line arguments and process each filename found.
 			CommandArgsReader commandArgs = new CommandArgsReader(mm);
@@ -116,12 +118,25 @@
 			commandArgs.setValidOptions(getValidOptions(System.err));
 			commandArgs.readArgs(args);
 		} catch (MapFailedException e) {
-			System.err.println(e.getMessage());
+			System.err.println(e.getMessage()); // should not happen
 		} catch (ExitException e) {
+			++numExitExceptions;
 			System.err.println(e.getMessage());
 		}
+		
+		System.out.println("Number of ExitExceptions: " + numExitExceptions);
+		
 		System.out.println("Time finished: " + new Date());
-		System.out.println("Total time taken: " + (System.currentTimeMillis() - start) + "ms"); 
+		System.out.println("Total time taken: " + (System.currentTimeMillis() - start) + "ms");
+		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+		if (numExitExceptions > 0 || mm.getProgramRC() != 0){
+			if (stackTraceElements.length > 2){
+				// Main is called from other java program, don't call System.exit()
+				// This happens in the unit tests
+				return; 
+			}
+			System.exit(1);
+		}
 	}
 	
 	private static void printUsage (){
@@ -128,6 +143,14 @@
 		System.err.println("Usage: mkgmap [options...] <file.osm>");
 	}
 
+	synchronized private void setProgramRC(int rc){
+		programRC = rc;
+	}
+
+	synchronized private int getProgramRC(){
+		return programRC;
+	}
+	
 	/**
 	 * Grab the options help file and print it.
 	 * @param err The output print stream to write to.
@@ -420,7 +443,9 @@
 
 
 		List<FilenameTask> filenames = new ArrayList<FilenameTask>();
-
+		
+		int numMapFailedExceptions = 0;
+		
 		if (threadPool != null) {
 			threadPool.shutdown();
 			while (!futures.isEmpty()) {
@@ -451,7 +476,9 @@
 				} catch (ExitException ee) {
 					throw ee;
 				} catch (MapFailedException mfe) {
-					System.err.println(mfe.getMessage());
+//					System.err.println(mfe.getMessage()); // already printed via log
+					numMapFailedExceptions++;
+					setProgramRC(-1);
 				} catch (Throwable t) {
 					t.printStackTrace();
 					if (!args.getProperties().getProperty("keep-going", false)) {
@@ -460,6 +487,7 @@
 				}
 			}
 		}
+		System.out.println("Number of MapFailedExceptions: " + numMapFailedExceptions);
 
 		if (combiners.isEmpty())
 			return;
@@ -543,6 +571,7 @@
 				new File(ovmFile).delete();
 			}
 		}
+		
 	}
 
 	private void fileOptions(CommandArgs args) {
Index: src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java	(revision 3258)
+++ src/uk/me/parabola/mkgmap/reader/polish/PolishMapDataSource.java	(working copy)
@@ -790,8 +790,7 @@
                             [END-RESTRICT]
                          */
                         restriction.setValid(false);
-                        log.info("Restrictions composed\n" +
-                                "from 3 roads are not yet supported\n");
+                        log.info("Restrictions composed from 3 or more roads are not yet supported");
                     }
                 } else if (name.equals("TraffRoads")) {
                     String[] traffRoads = value.split(",");
Index: src/uk/me/parabola/mkgmap/reader/polish/RoadHelper.java
===================================================================
--- src/uk/me/parabola/mkgmap/reader/polish/RoadHelper.java	(revision 3258)
+++ src/uk/me/parabola/mkgmap/reader/polish/RoadHelper.java	(working copy)
@@ -270,7 +270,7 @@
 			if (f.length > 2)
 				boundary = Integer.parseInt(f[2]) > 0;
 			if (log.isDebugEnabled())
-				log.debug("ind=%d, node=%d, bound=%b\n", index, nodeId, boundary);
+				log.debug("ind=" + index + "node=" + nodeId + "bound=" + boundary);
 		}
 
 		public String toString() {
