HEX
Server: Apache
System: Linux pdx1-shared-a1-38 6.6.104-grsec-jammy+ #3 SMP Tue Sep 16 00:28:11 UTC 2025 x86_64
User: mmickelson (3396398)
PHP: 8.1.31
Disabled: NONE
Upload Files
File: //usr/share/jed/lib/dired.sl
% -*- SLang -*-		dired.sl
%
%  Simple dired mode for JED.
%
%
% To invoke Dired, do `C-x d' or `M-x dired'.
%
%  Moving around
%  =============
%
%  All the usual motion commands plus some extras:
%
%  `C-n' `n' SPC
%       move point to the next line (at the beginning of the file name)
%
%  `C-p' `p'
%       move point to the previous line (at the beginning of the file name)
%
%  DEL
%       move up and unflag
%
%  `^K'
%	dired_kill_line - removes a line from the dired buffer
%       (This must be set from the dired hook)
%
%  ==============
%
%     The primary use of Dired is to "flag" files for deletion and then
%  delete the previously flagged files.
%
%  `d'
%       Flag this file for deletion.
%
%  `u'
%       Remove deletion flag on this line.
%
%  `DEL'
%       Move point to previous line and remove the deletion flag on that
%       line.
%
%  `~'
%       Flag all backup files (files whose names end with `~') for deletion
%
%  `g'
%       Update the entire contents of the Dired buffer
%
%  Deleting Files
%  ==============
%
%  `x'
%       expunge all flagged files.  Displays a list of all the file names
%       flagged for deletion, and requests confirmation with `yes'.
%       After confirmation, all the flagged files are deleted and then
%       their lines are deleted from the Dired buffer.
%
%  File Manipulations
%  ==================
%
%  `f'
%       Visit the file described on the current line, like typing `C-x C-f'
%       and supplying that file name
%
%  `v'
%       View the file described on the current line
%
%  `r'
%       rename - prompts for a new name for the file on the current line
%
%  'm'
%       move - move a group of flagged files to an new directory
%
% `M-x dired_search'
%       use fsearch with the contents of LAST_SEARCH to perform a search
%       through the files listed in the dired buffer from the current point
%       forward.  Since it stops in the file where the search string is
%       encountered, it is structured so that `M-x dired_search' from the
%       visited file will revert back to the dired buffer and continue the
%       dired_search from the next file in the list.
%

require ("glob");

variable Dired_Quick_Help = "d:tag file, u:untag, x:delete tagged files, r:rename, h:more help, ?:this help";
variable Dired_Buffer = "*dired*";
variable Dired_Current_Directory;
variable Dired_Move_Target_Dir;

ifnot (keymap_p (Dired_Buffer)) make_keymap (Dired_Buffer);

definekey ("dired_find",	"\r",	Dired_Buffer);
definekey ("dired_find",	"f",	Dired_Buffer);
definekey ("dired_view",	"v",	Dired_Buffer);
definekey ("dired_tag",		"d",	Dired_Buffer);
definekey (". 1 dired_untag",	"u",	Dired_Buffer);
definekey ("dired_move",	"m",	Dired_Buffer);
definekey ("dired_delete",	"x",	Dired_Buffer);
definekey (". 1 dired_point",	"^N",	Dired_Buffer);
definekey (". 1 dired_point",	"n",	Dired_Buffer);
definekey (". 1 dired_point",	" ",	Dired_Buffer);
definekey (". 1 chs dired_point",	"^P",	Dired_Buffer);
definekey (". 1 chs dired_point",	"p",	Dired_Buffer);
#ifdef UNIX
definekey (". 1 chs dired_untag",	"^?",	Dired_Buffer); % DEL key
#elifdef IBMPC_SYSTEM
definekey (". 1 chs dired_untag",	"\xE0S",Dired_Buffer);   %  DELETE
definekey (". 1 chs dired_untag",	"\eOn",	Dired_Buffer);   %  DELETE
#endif
definekey ("dired_flag_backup",	"~",	Dired_Buffer);
definekey ("dired_rename",	"r",	Dired_Buffer);
definekey ("dired_reread_dir",	"g",	Dired_Buffer);
definekey ("describe_mode",	"h",	Dired_Buffer);
definekey ("dired_quick_help",	"?",	Dired_Buffer);
definekey ("dired_quit",	"q",	Dired_Buffer);

define dired_quit ()
{
   delbuf (Dired_Buffer);
}

private define mode_to_modestring (st)
{
   variable i,
     mode = st.st_mode,
     is_types = ["reg", "dir", "lnk", "chr", "fifo", "sock", "blk"],
     type_codes = ['-', 'd', 'l', 'c', 'p', 's', 'b'];

   variable mode_string = Char_Type[10]; mode_string[*] = '-';

   _for i (0, length(is_types)-1, 1)
     {
	ifnot (stat_is (is_types[i], mode))
	  continue;
	mode_string[0] = type_codes[i];
	break;
     }

   if (mode & S_IRUSR) mode_string[1] = 'r';
   if (mode & S_IWUSR) mode_string[2] = 'w';
   if (mode & S_IXUSR) mode_string[3] = 'x';
#ifexists S_ISUID
   if (mode & S_ISUID) mode_string[3] = 's';
#endif

#ifdef __WIN32__
   variable opts = st.st_opt_attrs;
   if (opts & FILE_ATTRIBUTE_ARCHIVE) mode_string[7] = 'A';
   if (opts & FILE_ATTRIBUTE_SYSTEM) mode_string[8] = 'S';
   if (opts & FILE_ATTRIBUTE_HIDDEN) mode_string[9] = 'H';
#else
   if (mode & S_IRGRP) mode_string[4] = 'r';
   if (mode & S_IWGRP) mode_string[5] = 'w';
   if (mode & S_IXGRP) mode_string[6] = 'x';
# ifexists S_SISGID
   if (mode & S_ISGID) mode_string[6] = 'g';
# endif

   if (mode & S_IROTH) mode_string[7] = 'r';
   if (mode & S_IWOTH) mode_string[8] = 'w';
   if (mode & S_IXOTH) mode_string[9] = 'x';
# ifexists S_ISVTX
   if (mode & S_ISVTX) mode_string[9] = 't';
# endif
#endif
   return typecast (array_to_bstring (mode_string), String_Type);
}

private define escape_string (str)
{
   %  include > to encode symlink indicator
   variable badchars = "> \t\n\"";

   if (str == str_delete_chars (str, badchars))
     return str;

   variable newstr = "\"";
   badchars = "^" + badchars + "\\";
   variable i, i0 = 0, n = strbytelen (str);

   while (i = strskipbytes (str, badchars, i0), i < n)
     {
	variable ch = str[i];
	newstr = newstr + str[[i0:i-1]];
	newstr = newstr + sprintf ("\\x%02X", ch);
	i0 = i+1;
     }
   newstr = newstr + str[[i0:i-1]] + "\"";

   return newstr;
}

private define insert_directory_listing (pat)
{
   variable this_dir;
   variable st = stat_file (pat);
   if ((st != NULL) && (stat_is ("dir", st.st_mode)))
     {
	this_dir = pat;
	pat = [path_concat (pat, "*"), path_concat (pat, ".*")];
     }
   else
     this_dir = path_dirname (pat);

   variable file, files = glob (pat);
   variable i, n = length (files);
   variable stats = Struct_Type[n];
   variable sizes = UInt64_Type[n];

   files = files[array_sort (files)];
   _for i (0, n-1, 1)
     {
	file = files[i];
	st = lstat_file (file);
	if (st == NULL)
	  continue;
	stats[i] = st;
	sizes[i] = st.st_size;
     }

   variable max_size = 0;
   if (length (sizes))
     max_size = max(sizes);

   variable numdigits = 0;
   do
     {
	max_size /= 10;
	numdigits++;
     }
   while (max_size);

   variable fmt = "  %10s %6S %6S " + sprintf ("%%%dS ", numdigits)
     + "%s %s%s\n";

   variable mode_string = NULL, mode = NULL;
   variable six_months = _time () - 3600*24*30*6;

   _for i (0, n-1, 1)
     {
	st = stats[i];
	if (st == NULL)
	  continue;

	if (mode != st.st_mode)
	  {
	     mode = st.st_mode;
	     mode_string = mode_to_modestring (st);
	  }

	variable mtime, tm;

	mtime = st.st_mtime;
	tm = localtime (mtime);

	if (tm == NULL)
	  mtime = "Jan 01  1980";      %  Windows
	else
	  {
	     if (mtime < six_months)
	       mtime = strftime ("%b %d  %Y", tm);
	     else
	       mtime = strftime ("%b %d %H:%M", tm);
	  }
	variable symlink = "";

	file = files[i];
#ifexists readlink
	if (stat_is ("lnk", mode))
	  {
	     symlink = readlink (path_concat (this_dir, file));
	     if (symlink == NULL)
	       symlink = "??";

	     symlink = " -> " + escape_string (symlink);
	  }
#endif
	file = escape_string (path_basename (file));

	vinsert (fmt,
		 mode_string, st.st_uid, st.st_gid, st.st_size,
		 mtime, file, symlink);
     }
}


define dired_read_dir (dir)
{
   variable file, flags;

#ifdef IBMPC_SYSTEM
   dir = expand_filename (dir);
# ifdef MSDOS MSWINDOWS
   dir = msdos_fixup_dirspec (dir);
# endif
#endif

   variable want_dir;
   if ( file_status (dir) == 2 )
     {
	(want_dir,) = parse_filename (dircat (dir, Dired_Buffer));
#ifdef VMS
	dir = Dired_Current_Directory;
#endif
     }
   else
     {
	(want_dir,dir) = parse_filename (dir);
     }
   if ( change_default_dir (want_dir))
      error ("Failed to chdir.");
   Dired_Current_Directory = want_dir;
   sw2buf (Dired_Buffer);
   (file,,,flags) = getbuf_info ();
   setbuf_info (file, Dired_Current_Directory, Dired_Buffer, flags);
   set_status_line (" DIRED: %b   (%m%n)  (%p)  |  press '?' for help.", 0);
   set_readonly (0);
   erase_buffer ();
   use_keymap (Dired_Buffer);
   set_mode ("dired", 0);

   insert_directory_listing (dir);

   bob ();
   insert ("== ");
   insert (Dired_Current_Directory);
   newline ();
   set_buffer_modified_flag (0); set_readonly(1);
   flush ("");
}

define dired_quick_help ()
{
   message (Dired_Quick_Help);
}

define dired_reread_dir ()
{
   dired_read_dir (Dired_Current_Directory);
}

% set the point at the start of the file name
define dired_point (dirn)
{
   if (dirn > 0) go_down_1 (); else if (dirn < 0) go_up_1 ();

   bol_skip_white ();
   if (looking_at_char ('l'))
     {
	() = ffind ("->");
	bskip_white ();
     }
   else eol ();

   bskip_chars ("^ \n");
}

#ifndef VMS
define dired_kill_line ()
{
   bol ();
   if ( bobp () )  return;		% do not remove top line
   set_readonly (0);
   push_mark (); go_down_1 ();
   del_region ();
   set_buffer_modified_flag (0);
   set_readonly (1);
   dired_point (0);
}
#endif

private define extract_filename_at_point ()
{
   push_spot ();
   push_mark ();
   if (looking_at_char ('"'))
     {
	go_right (1);
	if (ffind_char ('"'))
	  go_right (1);
     }
   else skip_chars ("^ \t\n");

   variable name = strtrim (bufsubstr ());
   pop_spot ();

   if (name[0] == '"')
     {
	try
	  {
	     name = eval (name);
	  }
	catch AnyError: name = "";
     }
   return name;
}

% (name, type) = dired_getfile ()
%
% name = name of file or directory
%
% type = 0 : nothing
% type = 1 : file
% type = 2 : directory
% type = 3 : link
define dired_getfile ()
{
   variable name, type, ext, stat_buf;

   bol ();
   if (bobp ())
     return ("", 0);

   go_right(2);			       %  skip possible D tag

   type = 1;
   if (looking_at_char ('d')) type = 2;
   else if (looking_at_char ('l')) type = 3;

   dired_point (0);
   name = extract_filename_at_point ();
   if (type == 3)
     {
	% symbolic link
	stat_buf = stat_file (name);
	if (stat_buf != NULL)
	  {
	     if (stat_is ("dir", stat_buf.st_mode)) type = 2;
	     else if (stat_is ("reg", stat_buf.st_mode)) type = 1;
	  }
     }
   return (name, type);
}

define dired_tag ()
{
   variable type;
   EXIT_BLOCK { dired_point (1); }

   (, type) = dired_getfile ();
   if ( type != 1 ) return;		% only files!

   set_readonly (0);
   bol ();
   insert_char ('D'); del ();
   set_buffer_modified_flag (0);
   set_readonly (1);
}

define dired_untag (dirn)
{
   if ( dirn < 0 )
     {
	ifnot ( up_1 () ) error ("Top of Buffer.");
     }
   bol ();
   if ( looking_at_char ('D') )
     {
	set_readonly (0);
	insert_char (32); del ();
	set_buffer_modified_flag (0);
	set_readonly (1);
     }
   if ( dirn > 0 )
     dired_point (1);
}

% perform operation on tagged files--- 4 parameters
define dired_xop_tagged_files (prompt, msg, op_function)
{
   variable lbuf = " *Deletions*";
   variable stack, n, fails = Null_String;
   variable file;

   setbuf (Dired_Buffer);
   push_spot_bob ();

   stack = _stkdepth;			% save stack depth
   ERROR_BLOCK
     {
	_pop_n ( _stkdepth - stack );
	sw2buf (Dired_Buffer);
	set_readonly (0);
	bob ();
	while ( bol_fsearch_char ('%') )
	  {
	     insert_char ('D'); del ();
	  }
	pop_spot ();
	set_buffer_modified_flag (0); set_readonly (1);
     }

   set_readonly (0);

   while ( bol_fsearch_char ('D') )
     {
	insert_char ('%'); del ();
	dired_getfile ();
	pop ();			% pop type, leave name on stack
     }

   n = _stkdepth - stack;
   ifnot (n) error ("No tags!");

   sw2buf (lbuf);
   erase_buffer ();

   loop (n)
     {
	insert ();				% tagged files on stack
	newline ();
     }
   bob ();
   buffer_format_in_columns ();
   if ( get_yes_no (prompt) == 1)
     {
	sw2buf (Dired_Buffer);
	bob ();
	while ( bol_fsearch_char ('%') )
	  {
	     (file,) = dired_getfile ();
	     bol ();
	     push_spot ();
	     file = dircat (Dired_Current_Directory, file);
	     if (@op_function (file) )
	       {
		  pop_spot ();
		  flush (msg + file);
		  push_mark ();
		  go_down_1 ();
		  del_region (); go_left_1 ();
	       }
	     else
	       {
		  pop_spot ();
		  fails += " " + file;
		  insert_char (32); del ();
	       }
	  }
     }

   EXECUTE_ERROR_BLOCK;

   if ( strlen (fails) )
     message ("Operation Failed:" + fails);
}

define dired_delete ()
{
   dired_xop_tagged_files ("Delete these files", "Deleted ", &delete_file);
}

define dired_do_move (file)
{
   variable name;

   (name,) = dired_getfile ();
   name = dircat (Dired_Move_Target_Dir, name);

   not (rename_file (file, name));
}

define dired_move ()
{
   Dired_Move_Target_Dir = read_file_from_mini ("Move to dir");
   if ( file_status (Dired_Move_Target_Dir) != 2 )
     error ("Expecting directory name");

   "Move these to " + Dired_Move_Target_Dir;
   dired_xop_tagged_files ((), "Moved ", &dired_do_move);
}

#ifndef VMS
define dired_flag_backup ()
{
   variable name, type;

   push_spot_bob (); set_readonly (0);
   while ( fsearch_char ( '~' ) )
     {
	(name, type) = dired_getfile ();
	if ( (type == 1) and (string_match (name, "~", strlen(name))) )
	  {
	     bol ();
	     insert_char ('D'); del ();	% is a backup file
	  }
	eol ();
     }
   pop_spot ();
   set_buffer_modified_flag (0);
   set_readonly (1);
}
#endif

define dired_rename ()
{
   variable oldf, type, len, f, n, nf, nd, od, status;

   (oldf, type)  = dired_getfile ();
   ifnot ( type ) return;
   sprintf ("Rename %s to", oldf);
   n = read_file_from_mini (());
   %
   %  If new name is a dir, move it to the dir with oldname.
   %  If file is not a directory and exists, signal error.
   %
   status = file_status (n);
   if ( status == 1 )  error ("File exists. Not renamed.");
   else if ( status == 2 ) n = dircat (n, oldf);

   %
   %  Check to see if rename to new directory
   %
   (nd,nf) = parse_filename (n);
   f = dircat (Dired_Current_Directory, oldf);
   (od,) = parse_filename (f);
   if ( rename_file (f, n) ) error ("Operation Failed!");
   set_readonly (0);
#ifdef UNIX
   if (od != nd)
#elifdef VMS IBMPC_SYSTEM
   if (strup (od) != strup (nd))
#endif
     {
	delete_line ();
        dired_point (0);
     }
   else
     {
        dired_point (0);
	push_mark ();
	skip_chars ("^ \t\n");
	del_region ();
	insert (escape_string (nf));
	dired_point (1);
     }
   set_buffer_modified_flag (0);
   set_readonly (1);
}

%!%+
%\function{dired}
%\synopsis{dired}
%\description
% Mode designed for maintaining and editing a directory.
%
% To invoke Dired, do \var{M-x dired} or \var{C-x d} (emacs)
%
% Dired will prompt for a directory name and get a listing of files in the
% requested directory.
%
% The primary use of Dired is to "flag" files for deletion and then delete
% the previously flagged files.
%
% \var{d}	Flag this file for deletion.
% \var{u}	Remove deletion flag on this line.
% DEL	Move point to previous line and remove deletion flag.
% \var{~}	Flag all backup files for deletion.
%
% \var{x}	eXpunge all flagged files.  Dired will show a list of the
% 	files tagged for deletion and ask for confirmation before actually
% 	deleting the files.
%
% \var{r}	Rename file on the current line; prompts for a newname
% \var{m}	Move tagged files to a new dir; prompts for dir name
%
% \var{g}	Update the entire contents of the Dired buffer
%
% \var{f}	Visit the file described on the current line, like typing
% 	\var{M-x find_file} and supplying that file name.  If current line is a
% 	directory, runs dired on the directory and the old buffer is killed.
%
% \var{v}	View the file described on the current line in MOST mode.
%
% \var{q}	Quit dired mode.
%
% \var{M-x dired_search}
% 	use fsearch to perform a search through the files listed in the
% 	dired buffer from the current point forward.  \var{M-x dired_search}
% 	from the visited file will revert to the dired buffer and continue
% 	the search from the next file in the list.
%
% all the usual motion commands plus some extras:
%
% \var{C-n} \var{n} SPC
% 	move point to the next line (at the beginning of the file name)
%
% \var{C-p} \var{p}
% 	move point to the previous line (at the beginning of the file name)
%
% \var{M-x dired_kill_line}	\var{^K} (emacs)
% 	removes a line from the dired buffer
%!%-
define dired ()
{
   variable dir;
   if (_NARGS == 1)
     dir = ();
   else
     dir = read_file_from_mini ("Directory:");
   dired_read_dir (dir);
   dired_quick_help ();
   run_mode_hooks ("dired_hook");
}

define dired_find ()
{
   variable name, type;

   (name, type) = dired_getfile ();

   name = dircat (Dired_Current_Directory, name);

   if ( type == 1 )
     {
	ifnot ( read_file (name) ) error ("Unable to read file.");
	pop2buf (whatbuf ());
     }
   else if ( type == 2 )
     {
	dired_read_dir (name);
     }
}

define dired_view ()
{
   variable name, type;

   (name, type) = dired_getfile ();

   name = dircat (Dired_Current_Directory, name);
   if ( type == 1 )
     {
	ifnot ( read_file (name) ) error ("Unable to read file.");
	pop2buf (whatbuf ());
	most_mode ();
     }
}

#ifndef VMS
define dired_search_files ()
{
   go_right_1 ();		% start after this one
   if ( fsearch (LAST_SEARCH) )
     return (1);		% found - stop search

   if ( buffer_modified () ) error ("buffer has been modified");
   delbuf (whatbuf ());
   pop2buf (Dired_Buffer);
   return 0;
}

define dired_search ()
{
   variable str, name, type;

   ifnot ( bufferp (Dired_Buffer) ) error ( "*dired* not available.");

   if ( strcmp (Dired_Buffer, whatbuf () ) ) % continue last search
     {
	ifnot ( strlen (LAST_SEARCH) ) error ("No specified search string");
	if ( dired_search_files () ) return;
	go_down_1 (); 			% do the next file!
     }
   else
     {
	str = read_mini ("dired_search:", Null_String, LAST_SEARCH);
	ifnot ( strlen (str) ) error ("Specify search string");
	save_search_string (str);
     }

   do
     {
	(name, type) = dired_getfile ();
	if ( type == 1 ) 			% only search files
	  {
	     name = dircat (Dired_Current_Directory, name);
	     ifnot ( read_file (name) ) error ("Unable to read file.");
	     if ( dired_search_files () )
	       {
		  pop2buf (whatbuf ());
		  return;
	       }
	  }
     }
   while ( down_1 () );
}
#endif