diff --git a/exec/filearea-lb.js b/exec/filearea-lb.js
index 65f52349d6dfec724ae06722569b9e5025e046b3..aa5d2a05a00da5086d734cfd3ca96d6844ed9574 100644
--- a/exec/filearea-lb.js
+++ b/exec/filearea-lb.js
@@ -1,12 +1,24 @@
 // Lightbar file library/directory browser
 load("sbbsdefs.js");
+load("cga_defs.js"); // For color definitions
 load("file_size.js");
 load("filedir.js");
 load("frame.js");
 load("tree.js");
 load("scrollbar.js");
 
-// See exec/load/sbbsdefs.js for color names
+// File library index: bbs.curlib
+// File directory index: bbs.curdir
+// File directory internal code: bbs.curdir_code
+var gStartupDirCode = "";
+if (argv.length > 0)
+	gStartupDirCode = argv[0];
+
+// gViewableFileTypesCfg will be set to an array containing the
+// viewable file types
+var gViewableFileTypesCfg = null;
+
+// See exec/load/cfa_defs.js for color names
 var colors = {
 	'fg' : WHITE,      // Non-highlighted item foreground
 	'bg' : BG_BLACK,   // Non-highlighted item background
@@ -425,10 +437,8 @@ var fileChooser = function(code) {
 		)
 	);
 	state.chooser.footerFrame.putmsg(
-		format(
-			"[Enter] Info %s T)ag %s D)ownload %s U)pload %s G)o back %s %s",
-			divider, divider, divider, divider, divider, standardHelp
-		)
+		format("[Enter] Info %s T)ag %s V)iew %s D)load %s U)pload %s G)o back %s %s",
+		       divider, divider, divider, divider, divider, divider, standardHelp)
 	);
 }
 
@@ -509,7 +519,6 @@ var canDownload = function() {
 }
 
 var inputHandler = function(userInput) {
-
 	if(userInput == "")
 		return;
 
@@ -567,6 +576,49 @@ var inputHandler = function(userInput) {
 				fileChooser(state.dir);
 			}
 			break;
+		case "V": // View file
+			if(state.browse == BROWSE_FILES) {
+				ret = true;
+				// Populate gViewableFileTypesCfg if it hasn't been populated yet
+				if (gViewableFileTypesCfg == null)
+					gViewableFileTypesCfg = readViewableFileTypesConfig();
+				// Get the filename extension
+				var filenameExt = file_getext(state.chooser.tree.currentItem.fileItem.fullPath);
+				if (typeof(filenameExt) == "string")
+				{
+					filenameExt = filenameExt.substr(1);
+					// See if the filename extension is in the list of viewable file types
+					var filenameExtUpper = filenameExt.toUpperCase();
+					var fileViewCmd = "";
+					for (var i = 0; i < gViewableFileTypesCfg.length; ++i)
+					{
+						if (filenameExtUpper == gViewableFileTypesCfg[i].ext.toUpperCase())
+						{
+							// Get the file view command, and substitute %f or %s
+							// with the filename.
+							fileViewCmd = gViewableFileTypesCfg[i].viewCmd;
+							fileViewCmd = fileViewCmd.replace(/%f/i, state.chooser.tree.currentItem.fileItem.fullPath);
+							fileViewCmd = fileViewCmd.replace(/%s/i, state.chooser.tree.currentItem.fileItem.fullPath);
+							break;
+						}
+					}
+					// View the file if we got a file view command, or an eror if not.
+					console.print("\1n");
+					console.crlf();
+					if (fileViewCmd != "")
+						bbs.exec(fileViewCmd);
+					else
+					{
+						console.print("\1h\1yThat file is not viewable.\1n");
+						console.crlf();
+						console.pause();
+					}
+					console.clear(BG_BLACK|LIGHTGRAY);
+					frame.invalidate();
+					fileChooser(state.dir);
+				}
+			}
+			break;
 		default:
 			break;
 	}
@@ -592,9 +644,20 @@ var initDisplay = function() {
 }
 
 var init = function() {
-	bbs.sys_status|=SS_MOFF;
+	bbs.sys_status |= SS_MOFF;
 	initDisplay();
-	libraryChooser();
+	// If this script was given a directory code, and the code is valid, then
+	// list the files in that directory.  Otherwise, start with the file library
+	// chooser.
+	if ((gStartupDirCode != "") && dirCodeIsValid(gStartupDirCode))
+	{
+		fileChooser(gStartupDirCode);
+		state.dir = gStartupDirCode;
+		state.lib = file_area.dir[gStartupDirCode].lib_index;
+		state.browse = BROWSE_FILES;
+	}
+	else
+		libraryChooser();
 }
 
 var main = function() {
@@ -618,6 +681,118 @@ var cleanUp = function() {
 	exit(0);
 }
 
+// Reads the viewable file types configuration from Synchronet's file.cnf
+//
+// Return value: An array containing objects with the following properties:
+//               ext: The filename extension
+//               viewCmd: The command to view the file
+function readViewableFileTypesConfig()
+{
+	var viewableFileInfos = [];
+	var fileCnfFile = new File(system.ctrl_dir + "file.cnf");
+	if (fileCnfFile.open("r"))
+	{
+		// file.cnf should be read as in src\sbbs3\scfglib2.c
+		// in the read_file_config function.
+		// http://cvs.synchro.net/cgi-bin/viewcvs.cgi/src/sbbs3/scfglib2.c?view=log
+		// It can also help to see scfgdefs.h
+
+		////////////////////////////
+		// Extractable File Types //
+		////////////////////////////
+		var numExtFileTypesFilePos = 152;
+		fileCnfFile.position = numExtFileTypesFilePos;
+		var numExtractableFileTypes = fileCnfFile.readBin(2);
+		// Each extractable file type is 125 bytes
+		var extFileTypeRecordLen = 125;
+		// Skip past the extractable file type configs
+		fileCnfFile.position = numExtFileTypesFilePos + 2 + (numExtractableFileTypes * extFileTypeRecordLen);
+		/*
+		for (var i = 0; i < numExtractableFileTypes; ++i)
+		{
+			fileCnfFile.position = numExtFileTypesFilePos + 2 + (i * extFileTypeRecordLen);
+			var extFileType = fileCnfFile.read(extFileTypeRecordLen);
+			var extFileExt = getActualString(extFileType, 0, 3);
+			var extFileCmd = getActualString(extFileType, 4, extFileType.length);
+		}
+		*/
+
+		/////////////////////////////
+		// Compressable File Types //
+		/////////////////////////////
+		var numCompressableFileTypesFilePos = fileCnfFile.position;
+		var numCompressableFileTypes = fileCnfFile.readBin(2);
+		// Each extractable file type is 125 bytes
+		var cmpFileTypeRecordLen = 125;
+		// Skip past the compressable file type configs
+		fileCnfFile.position = numCompressableFileTypesFilePos + 2 + (numCompressableFileTypes * cmpFileTypeRecordLen);
+		
+		/////////////////////////
+		// Viewable File Types //
+		/////////////////////////
+		var numViewableFileTypesFilePos = fileCnfFile.position;
+		var numViewableFileTypes = fileCnfFile.readBin(2);
+		// A record for a viewable file type seems to be 125 bytes
+		const viewableFileTypeRecordLen = 125;
+		for (var i = 0; i <  numViewableFileTypes; ++i)
+		{
+			fileCnfFile.position = numViewableFileTypesFilePos + 2 + (i * viewableFileTypeRecordLen);
+			var fileInfo = fileCnfFile.read(viewableFileTypeRecordLen);
+			// Read the file extension & view command.  When we get
+			// to zeros in each one, then we know we've reached the
+			// end of the string.
+			var fileExt = getActualString(fileInfo, 0, 3);
+			var fileViewCmd = getActualString(fileInfo, 4, fileInfo.length);
+			viewableFileInfos.push({ "ext": fileExt, "viewCmd": fileViewCmd });
+		}
+
+		fileCnfFile.close();
+	}
+	return viewableFileInfos;
+}
+// Extracts a string from a string variable containing a string and zero-value bytes.
+//
+// Parameters:
+//  pStr: The string variable to extract from
+//  pStartIdx: The start index in the string variable
+//  pEndIdx: One past the last index in the string variable
+//
+// Return value: The string extracted from the given string variable
+function getActualString(pStr, pStartIdx, pEndIdx)
+{
+	var theStr = "";
+	for (var i = pStartIdx; i < pEndIdx; ++i)
+	{
+		if (ascii(pStr.charAt(i)) != 0)
+			theStr += pStr.charAt(i);
+		else
+			break;
+	}
+	return theStr;
+}
+
+// Returns whether or not an internal file directory code is valid.
+function dirCodeIsValid(pDirCode)
+{
+	if (typeof(pDirCode) != "string")
+		return false;
+
+	var dirCodeUpper = pDirCode.toUpperCase();
+	var codeIsValid = false;
+	for (var dirCode in file_area.dir)
+	{
+		if (dirCode.toUpperCase() == dirCodeUpper)
+		{
+			codeIsValid = true;
+			break;
+		}
+	}
+	return codeIsValid;
+}
+
+//////////////////////////////////////////////////
+// Main script code
+
 init();
 main();
 cleanUp();