diff --git a/src/sbbs3/listfile.cpp b/src/sbbs3/listfile.cpp
index ccf2fe950571d87fe622dcd1cb533b91ed5d691d..eed0552ab097db8066e3d3ae073c2a39aebce896 100644
--- a/src/sbbs3/listfile.cpp
+++ b/src/sbbs3/listfile.cpp
@@ -33,7 +33,7 @@ int extdesclines(char *str);
 /* list the directory header.                                                */
 /* Returns -1 if the listing was aborted, otherwise total files listed		 */
 /*****************************************************************************/
-int sbbs_t::listfiles(uint dirnum, const char *filespec, FILE* tofile, int mode)
+int sbbs_t::listfiles(const uint dirnum, const char *filespec, FILE* tofile, const int mode)
 {
 	char	hdr[256],letter='A';
 	uchar	flagprompt=0;
@@ -46,6 +46,20 @@ int sbbs_t::listfiles(uint dirnum, const char *filespec, FILE* tofile, int mode)
 	uint	file_row[BF_MAX];
 	size_t	longest = 0;
 
+	if(!tofile) {
+		action = NODE_LFIL;
+		curdirnum = dirnum;
+		if(cfg.listfiles_mod[0] && !listfiles_inside) {
+			char cmdline[256];
+
+			listfiles_inside = true;
+			snprintf(cmdline, sizeof(cmdline), "%s %s %u %s", cfg.listfiles_mod, cfg.dir[dirnum]->code, mode, filespec);
+			i=exec_bin(cmdline, &main_csi);
+			listfiles_inside = false;
+			return i;
+		}
+	}
+
 	if(!smb_init_dir(&cfg, &smb, dirnum))
 		return 0;
 	if(mode&FL_ULTIME) {
@@ -76,7 +90,6 @@ int sbbs_t::listfiles(uint dirnum, const char *filespec, FILE* tofile, int mode)
 	}
 
 	if(!tofile) {
-		action=NODE_LFIL;
 		getnodedat(cfg.node_num,&thisnode,0);
 		if(thisnode.action!=NODE_LFIL) {	/* was a sync */
 			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
@@ -330,7 +343,7 @@ int sbbs_t::listfiles(uint dirnum, const char *filespec, FILE* tofile, int mode)
 /* Prints one file's information on a single line                           */
 /* Return 1 if displayed, 0 otherwise										*/
 /****************************************************************************/
-bool sbbs_t::listfile(file_t* f, uint dirnum, const char *search, const char letter, size_t namelen)
+bool sbbs_t::listfile(file_t* f, const  uint dirnum, const char *search, const char letter, size_t namelen)
 {
 	char	*ptr;
 	bool	exist = true;
@@ -447,8 +460,8 @@ bool sbbs_t::listfile(file_t* f, uint dirnum, const char *search, const char let
 /* Returns -1 if 'Q' or Ctrl-C, 0 if skip, 1 if [Enter], 2 otherwise        */
 /* or 3, backwards. 														*/
 /****************************************************************************/
-int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, uint total
-							,int totalfiles)
+int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, const uint total
+							,const int totalfiles)
 {
 	char	ch,str[256],*p,remcdt=0,remfile=0;
 	int		c, d;
@@ -719,7 +732,7 @@ int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, uint* row, uint total
 /* action depending on 'mode.'                                              */
 /* Returns number of files matching filespec that were found                */
 /****************************************************************************/
-int sbbs_t::listfileinfo(uint dirnum, const char *filespec, int mode)
+int sbbs_t::listfileinfo(const uint dirnum, const char *filespec, const int mode)
 {
 	char	str[MAX_PATH + 1],path[MAX_PATH + 1],dirpath[MAX_PATH + 1],done=0,ch;
 	char 	tmp[512];
@@ -731,6 +744,19 @@ int sbbs_t::listfileinfo(uint dirnum, const char *filespec, int mode)
     file_t*	f;
 	struct	tm tm;
 
+	action = NODE_LFIL;
+	curdirnum = dirnum;
+
+	if(cfg.fileinfo_mod[0] && !listfileinfo_inside) {
+		char cmdline[256];
+
+		listfileinfo_inside = true;
+		snprintf(cmdline, sizeof(cmdline), "%s %s %u %s", cfg.fileinfo_mod, cfg.dir[dirnum]->code, mode, filespec);
+		i=exec_bin(cmdline, &main_csi);
+		listfileinfo_inside = false;
+		return i;
+	}
+
 	if(!smb_init_dir(&cfg, &smb, dirnum))
 		return 0;
 	if(smb_open_dir(&cfg, &smb, dirnum) != SMB_SUCCESS)
@@ -766,7 +792,6 @@ int sbbs_t::listfileinfo(uint dirnum, const char *filespec, int mode)
 			continue;
 		if((mode==FI_OLDUL || mode==FI_OLD) && f->hdr.when_written.time > ns_time)
 			continue;
-		curdirnum = dirnum;
 		if(mode==FI_OFFLINE && getfilesize(&cfg, f) >= 0)
 			continue;
 		if(mode==FI_USERXFER) {
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 407a7bf783a2bdbb0f4dfebfd5f41be43130bddb..bd8d4a270af4ffb84f1c9cf8edec7be47403b597 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1078,7 +1078,9 @@ public:
 	/* listfile.cpp */
 	bool	listfile(file_t*, uint dirnum, const char *search, const char letter, size_t namelen);
 	int		listfiles(uint dirnum, const char *filespec, FILE* tofile, int mode);
+	bool	listfiles_inside = false;
 	int		listfileinfo(uint dirnum, const char *filespec, int mode);
+	bool	listfileinfo_inside = false;
 	void	listfiletofile(file_t*, FILE*);
 	int		batchflagprompt(smb_t*, file_t* bf[], uint row[], uint total, int totalfiles);
 
@@ -1201,6 +1203,7 @@ public:
 	/* scandirs.cpp */
 	void	scanalldirs(int mode);
 	void	scandirs(int mode);
+	bool	scandirs_inside = false;
 
 	#define nosound()
 	#define checkline()
diff --git a/src/sbbs3/scandirs.cpp b/src/sbbs3/scandirs.cpp
index 30bbc2ee66f2e60da4cc4deaa3db5dd0c2e99019..5314a465e382db5821d6cfaf0992318540f9f0f0 100644
--- a/src/sbbs3/scandirs.cpp
+++ b/src/sbbs3/scandirs.cpp
@@ -31,6 +31,16 @@ void sbbs_t::scandirs(int mode)
 	int		s;
 	uint	i,k;
 
+	if(cfg.scandirs_mod[0] && !scandirs_inside) {
+		char cmdline[256];
+
+		scandirs_inside = true;
+		snprintf(cmdline, sizeof(cmdline), "%s 0 %u", cfg.scandirs_mod, mode);
+		exec_bin(cmdline, &main_csi);
+		scandirs_inside = false;
+		return;
+	}
+
 	if(!usrlibs) return;
 	mnemonics(text[DirLibOrAll]);
 	SAFEPRINTF2(keys, "%s%c\r", text[DirLibKeys], all_key());
@@ -104,6 +114,16 @@ void sbbs_t::scanalldirs(int mode)
 	int		s;
 	uint	i,j,k,d;
 
+	if(cfg.scandirs_mod[0] && !scandirs_inside) {
+		char cmdline[256];
+
+		scandirs_inside = true;
+		snprintf(cmdline, sizeof(cmdline), "%s 1 %u", cfg.scandirs_mod, mode);
+		exec_bin(cmdline, &main_csi);
+		scandirs_inside = false;
+		return;
+	}
+
 	if(!usrlibs) return;
 	k=0;
 	if(mode&FL_ULTIME) {			/* New file scan */
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index 32f71932a59cac19f5a74f049112c584e5d65f2c..4ea8165d4c0429208c5d2a4fe53fcd84f5b84840 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -2615,6 +2615,9 @@ void sys_cfg(void)
 					sprintf(opt[i++],"%-16.16s%s","List Nodes",cfg.nodelist_mod);
 					sprintf(opt[i++],"%-16.16s%s","Who's Online",cfg.whosonline_mod);
 					sprintf(opt[i++],"%-16.16s%s","Private Msg",cfg.privatemsg_mod);
+					sprintf(opt[i++],"%-16.16s%s","Scan Dirs",cfg.scandirs_mod);
+					sprintf(opt[i++],"%-16.16s%s","List Files",cfg.listfiles_mod);
+					sprintf(opt[i++],"%-16.16s%s","View File Info",cfg.fileinfo_mod);
 					sprintf(opt[i++],"%-16.16s%s","Temp Transfer",cfg.tempxfer_mod);
 					opt[i][0]=0;
 					uifc.helpbuf=
@@ -2624,28 +2627,31 @@ void sys_cfg(void)
 						"automatically loaded and executed during certain Terminal Server\n"
 						"operations.  Command-line arguments may be included for all.\n"
 						"\n"
-						"`Login`         Required module for interactive terminal logins (answer)\n"
-						"`Logon`         Executed during terminal logon procedure\n"
-						"`Sync`          Executed when terminal nodes are periodically synchronized\n"
-						"`Logoff`        Executed during terminal logoff procedure (interactive)\n"
-						"`Logout`        Executed during terminal logout procedure (offline)\n"
-						"`New User`      Executed at end of new terminal user creation process\n"
-						"`Expired User`  Executed during daily event when user expires (offline)\n"
-						"`Auto Message`  Executed when a user chooses to edit the auto-message\n"
-						"`Text Section`  Executed to handle general text file (viewing) section\n"
-						"`Xtrn Section`  Executed to handle external programs (doors) section\n"
-						"`Pre Xtrn`      Executed before external programs (doors) run\n"
-						"`Post Xtrn`     Executed after external programs (doors) run\n"
-						"`Temp Transfer` Temporary/archive file transfer menu\n"
-						"`Read Mail`     Executed when a user reads email/netmail\n"
-						"`Scan Msgs`     Executed when a user reads or scans a message sub-board\n"
-						"`Scan Subs`     Executed when a user scans one or more sub-boards for msgs\n"
-						"`List Msgs`     Executed when a user lists msgs from the msg read prompt\n"
-						"`List Logons`   Executed when a user lists logons ('-y' for yesterday)\n"
-						"`List Users`    Executed when a user lists the users of the system\n"
-						"`List Nodes`    Executed when a user lists all nodes\n"
-						"`Who's Online`  Executed when a user lists the nodes in-use (e.g. `^U`)\n"
-						"`Private Msg`   Executed when a user sends a private node msg (e.g. `^P`)\n"
+						"`Login`          Required module for interactive terminal logins (answer)\n"
+						"`Logon`          Terminal logon procedure\n"
+						"`Sync`           Terminal node is periodically synchronized\n"
+						"`Logoff`         Terminal logoff procedure (interactive)\n"
+						"`Logout`         Terminal logout procedure (offline)\n"
+						"`New User`       End of new terminal user creation process\n"
+						"`Expired User`   User account expires (offline)\n"
+						"`Auto Message`   User chooses to re-read or edit the auto-message\n"
+						"`Text Section`   Handle general text file (viewing) section\n"
+						"`Xtrn Section`   Handle external programs (doors) section\n"
+						"`Pre Xtrn`       Executed before external programs (doors) run\n"
+						"`Post Xtrn`      Executed after external programs (doors) run\n"
+						"`Read Mail`      User reads email/netmail\n"
+						"`Scan Msgs`      User reads or scans a message sub-board\n"
+						"`Scan Subs`      User scans one or more sub-boards for msgs\n"
+						"`List Msgs`      User lists msgs from the msg read prompt\n"
+						"`List Logons`    User lists logons ('-y' for yesterday)\n"
+						"`List Users`     User lists the users of the system\n"
+						"`List Nodes`     User lists all nodes\n"
+						"`Who's Online`   User lists the nodes in-use (e.g. `^U`)\n"
+						"`Private Msg`    User sends a private node msg (e.g. `^P`)\n"
+						"`Scan Dirs`      User scans one or more directories for files\n"
+						"`List Files`     User lists files within a file directory\n"
+						"`View File Info` User views detailed information on files in a directory\n" 
+						"`Temp Transfer`  Temporary/archive file transfer menu\n"
 						"\n"
 						"`Note:` JavaScript modules take precedence over Baja modules if both exist\n"
 						"      in your `exec` or `mods` directories.\n"
@@ -2742,6 +2748,18 @@ void sys_cfg(void)
 								,cfg.privatemsg_mod,sizeof(cfg.privatemsg_mod)-1,K_EDIT);
 							break;
 						case 21:
+							uifc.input(WIN_MID|WIN_SAV,0,0,"Scan Dirs Module"
+								,cfg.scandirs_mod,sizeof(cfg.scandirs_mod)-1,K_EDIT);
+							break;
+						case 22:
+							uifc.input(WIN_MID|WIN_SAV,0,0,"List Files Module"
+								,cfg.listfiles_mod,sizeof(cfg.listfiles_mod)-1,K_EDIT);
+							break;
+						case 23:
+							uifc.input(WIN_MID|WIN_SAV,0,0,"View File Information Module"
+								,cfg.fileinfo_mod,sizeof(cfg.fileinfo_mod)-1,K_EDIT);
+							break;
+						case 24:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Temporary File Transfer Module"
 								,cfg.tempxfer_mod, sizeof(cfg.tempxfer_mod)-1, K_EDIT);
 							break;
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 9200a66fce51469baefc61cabba58615284204a1..e13408484e4683d759e8c7a0d841f0caf7725b1d 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -608,6 +608,9 @@ typedef struct
 	char			scanposts_mod[LEN_CMD+1];	/* Scanning posts (in a single sub) module */
 	char			scansubs_mod[LEN_CMD+1];	/* Scanning sub-boards module */
 	char			listmsgs_mod[LEN_CMD+1];	/* Listing messages module */
+	char			scandirs_mod[LEN_CMD+1];
+	char			listfiles_mod[LEN_CMD+1];
+	char			fileinfo_mod[LEN_CMD+1];
 	char			nodelist_mod[LEN_CMD+1];
 	char			whosonline_mod[LEN_CMD+1];
 	char			privatemsg_mod[LEN_CMD+1];
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index b82117adcbd80b2d77446c272490b6d2a9aa0402..6458c47bb733c5f750d22cd7d5216065388f2ec6 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -249,6 +249,9 @@ BOOL read_main_cfg(scfg_t* cfg, char* error, size_t maxerrlen)
 	SAFECOPY(cfg->logonlist_mod, iniGetString(section, NULL, "logonlist", "logonlist", value));
 	SAFECOPY(cfg->prextrn_mod, iniGetString(section, NULL, "prextrn", "prextrn", value));
 	SAFECOPY(cfg->postxtrn_mod, iniGetString(section, NULL, "postxtrn", "postxtrn", value));
+	SAFECOPY(cfg->scandirs_mod, iniGetString(section, NULL, "scandirs", "", value));
+	SAFECOPY(cfg->listfiles_mod, iniGetString(section, NULL, "listfiles", "", value));
+	SAFECOPY(cfg->fileinfo_mod, iniGetString(section, NULL, "fileinfo", "", value));
 	SAFECOPY(cfg->tempxfer_mod, iniGetString(section, NULL, "tempxfer", "tempxfer", value));
 
 	/*******************/
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 2f18d58708f65a713685041836a7903eed2b5508..191ed62036fa01608b6b81ddfbd14b88a2086ea3 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -247,7 +247,6 @@ BOOL write_main_cfg(scfg_t* cfg, int backup_level)
 		iniSetString(&ini, name, "listmsgs", cfg->listmsgs_mod, NULL);
 		iniSetString(&ini, name, "textsec", cfg->textsec_mod, NULL);
 		iniSetString(&ini, name, "automsg", cfg->automsg_mod, NULL);
-		iniSetString(&ini, name, "xtrnsec", cfg->xtrnsec_mod, NULL);
 		iniSetString(&ini, name, "userlist", cfg->userlist_mod, NULL);
 
 		iniSetString(&ini, name, "nodelist", cfg->nodelist_mod, NULL);
@@ -255,9 +254,13 @@ BOOL write_main_cfg(scfg_t* cfg, int backup_level)
 		iniSetString(&ini, name, "privatemsg", cfg->privatemsg_mod, NULL);
 		iniSetString(&ini, name, "logonlist", cfg->logonlist_mod, NULL);
 
+		iniSetString(&ini, name, "xtrnsec", cfg->xtrnsec_mod, NULL);
 		iniSetString(&ini, name, "prextrn", cfg->prextrn_mod, NULL);
 		iniSetString(&ini, name, "postxtrn", cfg->postxtrn_mod, NULL);
 
+		iniSetString(&ini, name, "scandirs", cfg->scandirs_mod, NULL);
+		iniSetString(&ini, name, "listfiles", cfg->listfiles_mod, NULL);
+		iniSetString(&ini, name, "fileinfo", cfg->fileinfo_mod, NULL);
 		iniSetString(&ini, name, "tempxfer", cfg->tempxfer_mod, NULL);
 	}