Un-ignored: the dev drive is the ground truth the restoration and emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio content). Kept in-repo for the pod-owner community. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
313 lines
7.8 KiB
Plaintext
313 lines
7.8 KiB
Plaintext
/*
|
|
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
|
|
**
|
|
** Written by Dave Nanian and Michael Strickman.
|
|
*/
|
|
|
|
/*
|
|
** autosave.cb:
|
|
**
|
|
** This macro implements a simple autosave feature using the BRIEF
|
|
** idle action registered macro facility. Every time the user is idle
|
|
** for the amount of time specified by -i<num>, the buffer list is checked
|
|
** and modified files are automatically written to disk as "filename.asv"
|
|
** (stands for AutoSaVe).
|
|
**
|
|
** Autosaving can be forced after a number of minutes by passing the
|
|
** number of minutes between forced autosaves to the autosave macro. Note
|
|
** that this type of autosave can not be interrupted, and could become quite
|
|
** annoying if the value is too low.
|
|
*/
|
|
|
|
string escape_re (string original, ~string);
|
|
|
|
int force_autosave, // Whether or not we should save unmodified files.
|
|
save_buf, // Buffer that stores saved names.
|
|
last_idle_save, // Last idle time we saved.
|
|
last_save_time, // Last real time we saved.
|
|
last_save_day; // Last day we saved (to deal with rollover).
|
|
|
|
void autosave (~int)
|
|
{
|
|
int hours,
|
|
minutes;
|
|
|
|
register_macro (4, "save_files");
|
|
get_parm (0, force_autosave);
|
|
date (NULL, NULL, last_save_day);
|
|
time (hours, minutes);
|
|
last_save_time = minutes + (hours * 60);
|
|
}
|
|
|
|
/*
|
|
** save_files:
|
|
**
|
|
** This macro loops through the buffer list, writing all of the modified
|
|
** buffers. Note that a special buffer is created to keep track of the
|
|
** autosaved files.
|
|
*/
|
|
|
|
void save_files ()
|
|
{
|
|
int curr_buf,
|
|
old_buf,
|
|
saved,
|
|
do_autosave,
|
|
force_save,
|
|
hours,
|
|
minutes,
|
|
day;
|
|
|
|
date (NULL, NULL, day);
|
|
time (hours, minutes);
|
|
force_save = force_autosave && (minutes + ((hours + (day != last_save_day ? 24 : 0)) * 60)) - last_save_time >= force_autosave;
|
|
|
|
if (inq_idle_time () < last_idle_save)
|
|
last_idle_save = 0;
|
|
|
|
if (force_save || inq_idle_time () > inq_idle_default () && last_idle_save == 0)
|
|
{
|
|
last_idle_save = inq_idle_time ();
|
|
curr_buf = inq_buffer ();
|
|
|
|
/*
|
|
** This loop goes through the buffer list, and stops when
|
|
** all non-system buffers have been checked.
|
|
**
|
|
** Note that if this is an idle save and the user starts
|
|
** typing, the loop stops. This prevents the autosave from
|
|
** continuing when the user has made the transition from an
|
|
** idle to a non-idle state.
|
|
**
|
|
** If this is a forced save, all buffers are saved before
|
|
** stopping.
|
|
*/
|
|
|
|
while (!(curr_buf == old_buf || !force_save && inq_kbd_char ()))
|
|
{
|
|
if (!old_buf)
|
|
old_buf = curr_buf;
|
|
|
|
if (inq_modified () && !inq_system ())
|
|
{
|
|
/*
|
|
** If the current buffer is modified, and is not a system
|
|
** buffer, we have to write it to disk.
|
|
*/
|
|
|
|
string full_name,
|
|
just_name,
|
|
extension;
|
|
|
|
inq_names (full_name, extension, just_name);
|
|
|
|
if (strlen (extension))
|
|
full_name = substr (full_name, 1, rindex (full_name, "." + extension) - 1);
|
|
|
|
save_position ();
|
|
top_of_buffer ();
|
|
drop_anchor (3);
|
|
|
|
if (!save_buf)
|
|
{
|
|
set_buffer (save_buf = create_buffer ("Autosaves", NULL, 1));
|
|
delete_line ();
|
|
register_macro (5, "save_exit");
|
|
}
|
|
else
|
|
set_buffer (save_buf);
|
|
|
|
top_of_buffer ();
|
|
|
|
/*
|
|
** If the file has been autosaved already, we
|
|
** have some additional work to do.
|
|
*/
|
|
|
|
if (search_fwd (escape_re (full_name) + ".as[v0-9]\xff" + escape_re (extension) + "\n"))
|
|
{
|
|
full_name = read ();
|
|
full_name = substr (full_name, 1, index (full_name, "\xff") - 1);
|
|
|
|
/*
|
|
** If this is a forced save, we do it
|
|
** whether the files are the same or not.
|
|
*/
|
|
|
|
if (force_save)
|
|
{
|
|
del (full_name);
|
|
++do_autosave;
|
|
}
|
|
else
|
|
{
|
|
int comp_buf,
|
|
done;
|
|
|
|
string line_1,
|
|
line_2;
|
|
|
|
/*
|
|
** At this point, we know the file has
|
|
** been saved once before. Here, we check
|
|
** to see if the file has actually been
|
|
** modified since the last autosave. This
|
|
** is done by performing a line-by-line
|
|
** comparison of the two files. Note that
|
|
** this comparison (and the entire autosave)
|
|
** is aborted if the users starts typing.
|
|
*/
|
|
|
|
if (comp_buf = create_buffer ("Save Comp", full_name, 1))
|
|
{
|
|
while (!(inq_kbd_char () || done))
|
|
{
|
|
set_buffer (curr_buf);
|
|
|
|
/*
|
|
** If we're past the end of the
|
|
** file, we use the null string rather
|
|
** than the newline character that read
|
|
** usually returns. If this wasn't
|
|
** done, files with newline characters
|
|
** at their ends would match files
|
|
** that were essentially the same but
|
|
** shorter.
|
|
*/
|
|
|
|
if (inq_position ())
|
|
line_1 = "";
|
|
else
|
|
{
|
|
line_1 = read ();
|
|
move_rel (1, 0);
|
|
}
|
|
set_buffer (comp_buf);
|
|
|
|
if (inq_position ())
|
|
line_2 = "";
|
|
else
|
|
{
|
|
line_2 = read ();
|
|
move_rel (1, 0);
|
|
}
|
|
done += line_1 != line_2 || line_1 == "";
|
|
}
|
|
set_buffer (curr_buf);
|
|
delete_buffer (comp_buf);
|
|
|
|
/*
|
|
** We want to actually perform the save
|
|
** if the users hasn't typed anything and
|
|
** the last lines compared didn't match.
|
|
*/
|
|
|
|
if (!inq_kbd_char () && line_1 != line_2)
|
|
{
|
|
++do_autosave;
|
|
|
|
/*
|
|
** We delete the previously saved
|
|
** file at this point. If we didn't,
|
|
** a backup file could be created.
|
|
*/
|
|
|
|
del (full_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** To avoid problems with buffers that have the same base name
|
|
** but multiple extensions, we check to see if we've already saved
|
|
** a buffer with the intended autosave filename. If so, we construct
|
|
** a new extension using a number as the last character. This
|
|
** method handles up to ten different files in the same directory
|
|
** with the same base name, which will, hopefully, be plenty.
|
|
*/
|
|
|
|
int save_pass;
|
|
|
|
do
|
|
{
|
|
top_of_buffer ();
|
|
}
|
|
while (search_fwd (full_name + ".as" + substr ("v1234567890", ++save_pass, 1) + "\xff", 0));
|
|
|
|
full_name += ".as" + substr ("v1234567890", save_pass, 1);
|
|
|
|
insert (full_name + "\xff" + extension + "\n");
|
|
++do_autosave;
|
|
}
|
|
if (do_autosave)
|
|
{
|
|
++saved;
|
|
set_buffer (curr_buf);
|
|
message ("Autosaving %s...", just_name);
|
|
end_of_buffer ();
|
|
|
|
if (write_block (full_name) <= 0)
|
|
raise_anchor ();
|
|
|
|
--do_autosave;
|
|
}
|
|
else
|
|
raise_anchor ();
|
|
|
|
restore_position ();
|
|
}
|
|
/*
|
|
** When we go to the next buffer, we ask next buffer for
|
|
** all of the buffers, not just non-system. This way, if we
|
|
** started with a system buffer, we don't infinite loop.
|
|
*/
|
|
|
|
set_buffer (curr_buf = next_buffer (1));
|
|
}
|
|
if (old_buf)
|
|
set_buffer (old_buf);
|
|
|
|
if (force_save || saved)
|
|
{
|
|
if (force_save || !inq_kbd_char ())
|
|
{
|
|
last_save_day = day;
|
|
last_save_time = minutes + hours * 60;
|
|
}
|
|
if (saved)
|
|
message ("Autosave complete.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** save_exit:
|
|
**
|
|
** This routine is called just before BRIEF is going to exit for real.
|
|
** At that time, all of the autosaved files are deleted from the disk
|
|
** since they are no longer needed.
|
|
*/
|
|
|
|
void save_exit (void)
|
|
{
|
|
if (save_buf)
|
|
{
|
|
string name;
|
|
|
|
int old_buf = set_buffer (save_buf);
|
|
|
|
top_of_buffer ();
|
|
|
|
while (!inq_position ())
|
|
{
|
|
name = read ();
|
|
del (substr (name, 1, index (name, "\xff") - 1));
|
|
down ();
|
|
}
|
|
set_buffer (old_buf);
|
|
}
|
|
unregister_macro (4, "save_files");
|
|
}
|