#!/bin/sh

#
# frames - A frame range tool
#
#       erco@3dsite.com (Greg Ercolano)
#
#       This software is public domain. Please maintain version history.
#       Please notify me of bugs. Do not remove this header.
#       Use at your own risk.
#
#       VERS    DATE            AUTHOR          COMMENTS
#       1.00    11/17/94        Greg Ercolano   Initial version
#       1.10    05/28/98        Greg Ercolano   Added -rm
#       x.xx    xx/xx/xx        -		-
#	

awk '

# RETURN #SPACES EXACT LENGTH OF STRING 's'
function Spaces(s)
{
    return(substr(sprintf("%80s",""), 1, length(s)));
}

function HelpAndExit()
{
print "NAME"
print "    frame - a frame renumbering tool"
print ""
print "OPERATIONS [one must be specified]"
print "    -m[ove]   oldrange newrange   - Move frame range"
print "    -l[ink]   oldrange newrange   - Links"
print "    -s[link]  oldrange newrange   - Symbolic Links"
print "    -e[xist]  range               - Existance test a frame range"
print "    -r[m]     range               - Remove a frame range"
#print "*   -g[raph]  range               - Graph frame range sizes"
print "    range -exec cmd %s [%s..]     - Execute a command on a frame range"
print ""
print "OPTIONS"
print "    -d[ebug]               - debug [dont do anything - just print]"
print "    -q[uiet]               - quiet"
print ""
print "FRAME RANGES"
print "    A range can be:"
print ""
print "        foo.%d.rgb 1 10		- foo.1.rgb, foo.2.rgb.."
print "        foo.%04d.rgb 1 10 2	- foo.0001.rgb, foo.0003.rgb.."
print "        foo.%03d.rgb 1 10 2	- foo.001.rgb, foo.003.rgb.."
print ""
print "EXAMPLES"
print "    RENAME FRAMES. Renames one range of frames to another."
print "    The following changes the basename from foo to bla:"
print ""
print "         "progname" -move foo.%04d.rla 1 10 bla.%04d.rla"
print "         "Spaces(progname) \
			 "       ----------------- ------------";
print "         "Spaces(progname) \
		         "       Source Range      Dest Name";
print ""
print "    SHIFT FRAMES. Shifts frames 1 thru 10 to 21 thru 30:"
print ""
print "         "progname" -move foo.%04d.rla 1 10 foo.%04d.rla 21"
print "         "Spaces(progname) \
			 "       ----------------- ---------------";
print "         "Spaces(progname) \
		         "       Source Range      Dest Range";
print ""
print "    EXECUTE COMMAND. Executes imconv(1) to convert frames 1 thru 10"
print "    from rla files to rgb files:"
print ""
print "         "progname" foo.%04d 1 10 -exec imconv %s.rla %s.rgb"
print ""
print "    SYMBOLIC LINKS. Create 100 symbolic links that alternatly link"
print "    between a.0001.rgb and a.0002.rgb:"
print ""
print "         "progname" -slink a.%04d.rgb 1 2 b.%04d.rgb 1 100"
print ""
print "    REMOVE. Removes frames:"
print ""
print "         "progname" -rm a.%04d.rgb 1 30"
print ""
#print "    GRAPH. Graphs the frame sizes for a range of frames. Lets you"
#print "    do a quick check for bad frames:"
#print ""
#print "         "progname" -graph a.%04d.rgb 1 30"
#print "         "progname" -graph a.%04d.rgb 1 30 -rep   # repeat every 10 secs"
#print ""
print "To SEE what these commands do without running them, use '-debug'"
print "and the equivalent unix commands are printed to stdout."
exit(1);
}

# DOES FILE EXIST?
function Exist(filename)
{
    return( system("if [ -f "filename" ]; then exit 1; fi") );
}

# GET FILE SIZE
function Size(filename)
{
    $1 = "";
    cmd = "du -sL " filename;
    cmd | getline;
    close(cmd);
    if ( $1 == "" ) return("");
    return($1);
}

# SHOW THE FRAME RANGES (FOR DEBUGGING)
function ShowRanges()
{
    for (t=0; t<trange; t++)
    {
	print t ") RANGE BASE: " rangebase[t];
	print t ") RANGE SFRM: " rangesfrm[t];
	print t ") RANGE EFRM: " rangeefrm[t];
	print t ") RANGE IFRM: " rangeifrm[t];
	print ""
    }
}

# PRINT DEBUG MESSAGES IN SYNC WITH SHELL COMMANDS
function DebugMsg(debug_msg)
{
    if (quiet) return;
    if (debug) { print debug_msg; return; }
    print debug_msg | shellcmd;
}

# PRINT ERROR MESSAGES TO STDERR
function PrintError(msg)
{
    cmd = "cat 1>&2";
    print progname": "msg | cmd;
    close(cmd);
}

# RUN THE COMMAND
#	If debug enabled, only prints the command
#
function Run(execute_cmd)
{
    if (debug) { print execute_cmd; return; }

    # RUN THE COMMANDS IN A SHELL
    #	Use the CSH to print the commands, to keep error msgs in sync
    #
    print execute_cmd | shellcmd;
}

# GET THE NEXT FILENAME FOR RANGE INDEX
function NextFrame(i)
{
    nextfilename = sprintf(rangebase[i], thisframe[i]);
    thisframe[i] += rangeifrm[i];
    return(nextfilename);
}

# REWIND THE COUNTER
function RangeRewind(i)
{
    thisframe[i] = rangesfrm[i];
}

# SEE IF WE HIT THE END OF THE RANGE
function EndOfRange(i)
{
    # NO END FRAME?
    #	Then we are a secondary range that should be slaved 
    #	by the primary range end frame anyway.
    #
    if (rangeefrm[i] == "") return(0);

    # HIT END OF RANGE?
    if ( (rangeifrm[i] > 0 && thisframe[i] > rangeefrm[i]) || \
	 (rangeifrm[i] < 0 && thisframe[i] < rangeefrm[i]))
    {
	return(1);
    }
    return(0);
}

# INIT THIS RANGE TO UNSPECIFIED STATE
function CompileRangeStart(i)
{
    if (i==-1) return;
    rangebase[i] = "";
    rangesfrm[i] = "";
    rangeefrm[i] = "";
    rangeifrm[i] = "";
}

# COMPILE A FRAME RANGE
function CompileRange(i, s)
{
    if (i==-1) return;
    if (rangebase[i] == "") { rangebase[i] = s;     return; }
    if (rangesfrm[i] == "") { rangesfrm[i] = s + 0; return; }
    if (rangeefrm[i] == "") { rangeefrm[i] = s + 0; return; }
    if (rangeifrm[i] == "") { rangeifrm[i] = s + 0; return; }
    PrintError("\"" s "\": too many args for frame range");
    exit(1);
}

# END THE COMPILATION OF A RANGE
#	Note that unspecified start/end/inc in secondary ranges
#	assume the defaults of the values in the primary frame range.
#	This allows for shorthand such as:
#
#		frame -move old.%d.rla 101 200 , new.%d.rla
#
function CompileRangeEnd(i, defaults)
{
    if (i==-1) return;

    # NO BASENAME?
    if (rangebase[i] == "")
        { PrintError("no basename specified"); exit(1); }

    # START FRAME UNSPECIFIED?
    if (rangesfrm[i] == "")
    {
	if (i==0) { PrintError("no start frame"); exit(1); }
	rangesfrm[i] = rangesfrm[0];
    }

    # END FRAME UNSPECIFIED?
    if (rangeefrm[i] == "")
    {
	if (i==0)
	    rangeefrm[i] = rangesfrm[i];	# PRIMARY: assume start frame
	else
	    { }					# SECONDARY: assume NOTHING!
						# Handle it in EndOfRange()
    }

    # FRAME INC UNSPECIFIED?
    if (rangeifrm[i] == "")
    {
	if (i==0)
	    rangeifrm[i] = 1;			# PRIMARY: assume 1
	else
	    rangeifrm[i] = rangeifrm[0];	# SECONDARY: assume primary inc
    }

    # START WITH THE START FRAME
    thisframe[i] = rangesfrm[i];

    # SANITY CHECK FOR FRAME INCREMENT
    if (rangeefrm[i] != "")
    {
	if ((rangesfrm[i] > rangeefrm[i] && rangeifrm[i] > 0) || \
	    (rangesfrm[i] < rangeefrm[i] && rangeifrm[i] < 0))
	{
	    rangeifrm[i] = -rangeifrm[i];
	}
    }
}

# EXEC OPERATION
function Exec()
{
    if (trange != 1)
	{ PrintError("\"-exec\" only expects one range"); exit(1); }

    # LOOP THRU FRAME RANGES
    DebugMsg("\n#\n# EXEC: START\n#");
    while ( 1 )
    {
	if (EndOfRange(0)) break;
	filename = NextFrame(0);

	Run(sprintf(execcmd, filename, filename, filename, filename));
    }
    close (shellcmd);

    DebugMsg("\n#\n# DONE\n#");
    exit(0);
}

# MOVE OPERATION
function Move()
{

    if (trange != 2)
	{ PrintError("\"-move\" expects two frame ranges"); exit(1); }

    DebugMsg("\n### MOVE: SAFE NAMES\n");
    while ( 1 )
    {
	if (EndOfRange(0)) break;
	infile = NextFrame(0);

	Run(sprintf("mv %s __safe__%s", infile, infile));
    }

    DebugMsg("\n### MOVE: FINAL NAMES\n");
    RangeRewind(0);
    RangeRewind(1);
    while ( 1 )
    {
	if (EndOfRange(0)) break;
	if (EndOfRange(1)) RangeRewind(1);	# loop the secondary range 
						# if too short
	infile  = NextFrame(0);
	outfile = NextFrame(1);

	Run(sprintf("mv __safe__%s %s", infile, outfile));
    }

    DebugMsg("\n### MOVE: DONE\n");
    exit(0);
}

# REMOVE OPERATION
function Remove()
{
    if (trange != 1)
	{ PrintError("\"-rm\" expects only one frame range"); exit(1); }

    DebugMsg("\n### REMOVE FILES\n");

    while ( 1 )
    {
	if (EndOfRange(0)) break;
	infile  = NextFrame(0);
	Run(sprintf("rm %s", infile));
    }

    DebugMsg("\n#\n# DONE\n#");

    exit(0);
}

# LINK OPERATION
function Link()
{
    if (trange != 2)
	{ PrintError("\"-link\" expects two frame ranges"); exit(1); }

    DebugMsg("\n### CREATE HARD LINKS\n");

    while ( 1 )
    {
	if (EndOfRange(0)) break;
	infile  = NextFrame(0);
	outfile = NextFrame(1);

	Run(sprintf("ln %s %s", infile, outfile));
    }

    DebugMsg("\n#\n# DONE\n#");

    exit(0);
}

# SYMBOLIC LINK OPERATION
function Slink()
{
    if (trange != 2)
	{ PrintError("\"-slink\" expects two frame ranges"); exit(1); }

    DebugMsg("\n### CREATE SYMBOLIC LINKS\n");

    while ( 1 )
    {
	if (EndOfRange(0)) break;
	infile  = NextFrame(0);
	outfile = NextFrame(1);

	Run(sprintf("ln -s %s %s", infile, outfile));
    }

    DebugMsg("\n#\n# DONE\n#");

    exit(0);
}

# EXISTANCE CHECK
function ExistCheck()
{
    if (trange < 1)
	{ PrintError("\"-exist\" expects at least one frame range"); exit(1); }

    # LOOP THRU ALL RANGES SPECIFIED
    for (t=0; t<trange; t++)
    {
	DebugMsg(sprintf("\n### EXISTANCE CHECK: %s %s-%s\n", \
	    rangebase[t], rangesfrm[t], rangeefrm[t]));

	while ( 1 )
	{
	    if (EndOfRange(t)) break;
	    infile  = NextFrame(t);
	    printf("%20s: %s\n",
		infile, (Exist(infile) ? "OK" : "NOT FOUND"));
	}
    }

    DebugMsg("\n#\n# DONE\n#");

    exit(0);
}

# GRAPH OPERATION
function Graph()
{
    if (trange != 1)
	{ PrintError("\"-graph\" only expects one range"); exit(1); }

    tarr   = 0;
    max    = 1;

    # LOOP THRU FRAME RANGE
    RangeRewind(0);
    while ( 1 )
    {
	f_arr[tarr] = thisframe[0];
	if (EndOfRange(0)) break;
	filename = NextFrame(0);
	s_arr[tarr] = Size(filename);
	if ( s_arr[tarr] > max )
	    max = s_arr[tarr];
	tarr++;
    }

    hashes = "########################################";
    hashes = hashes hashes;

    if ( repeat )
	system("clear");

    # GRAPH FRAME SIZES
    for (t=0; t<tarr; t++)
    {
	if ( s_arr[t] == "" )
	{
	    printf("%04d: ???\n", f_arr[t]);
	    continue;
	}
	cnt = (s_arr[t] / max) * 70.0;
	printf("%04d: %s\n", f_arr[t], substr(hashes, 1, cnt));
    }

    if ( repeat ) 
	system("date +---%c; sleep 10");
}

BEGIN {
    progname = "frames";
    op       = "none";
    trange   = -1;
    quiet    = 0;
    debug    = 0;
    execflag = 0;
    argc     = ARGC;
    shellcmd = "/bin/sh -v";			# shell we pipe our commands to


    # PARSE DEBUG FLAGS FIRST
    for (t=1; t<argc; t++)
    {
	argv[t] = ARGV[t]; delete ARGV[t];
	if (argv[t] == "-d" || argv[t] == "-debug") { debug = 1; }
	if (argv[t] == "-exec") { execflag = 1; }
    }

    if (argc<2) HelpAndExit();

    for (t=1; t<argc; t++)
    {
	# EXEC OP SPECIFIED? EAT UP ALL THE REST OF THE ARGS AS CMD TO EXEC
	if ( op == "exec")
	    { execcmd = execcmd ((execcmd=="") ? "" : " ") argv[t]; continue; }

	if (argv[t] == "-"  || argv[t] == "-help" || argv[t] == "-h") 
	    { HelpAndExit(); }

	if (argv[t] == "-exec")
	    { op="exec"; continue; }

	if (argv[t] == "-d"  || argv[t] == "-debug")  {             continue; }
	if (argv[t] == "-m"  || argv[t] == "-move")   { op="move";  continue; }
	if (argv[t] == "-l"  || argv[t] == "-link")   { op="link";  continue; }
	if (argv[t] == "-s"  || argv[t] == "-slink")  { op="slink"; continue; }
	if (argv[t] == "-e"  || argv[t] == "-exist")  { op="exist"; continue; }
	if (argv[t] == "-rm" || argv[t] == "-remove") { op="rm";    continue; }
	if (argv[t] == "-g"  || argv[t] == "-graph")  { op="graph"; continue; }
	if (argv[t] == "-q"  || argv[t] == "-quiet")  { quiet=1;    continue; }
	if (argv[t] == "-rep"|| argv[t] == "-repeat") { repeat=1;   continue; }

	if (op == "none" && !execflag)
	{
	    PrintError("You must supply the operation before specifying `" \
		       argv[t]"`");
	    PrintError("(for instance, `"progname" -[operation] " \
		       argv[t]" ..`)");
	    exit(1);
	}

	# IF ARG CONTAINS A "%", ASSUME A NEW RANGE SPEC
	if (match(argv[t], "%"))
	{
	    CompileRangeEnd(trange);
	    CompileRangeStart(++trange);
	}
	CompileRange(trange, argv[t]);
    }
    CompileRangeEnd(trange);
    trange++;

    # DO THE OPERATION SPECIFIED
    if (debug)
	print "\n# NOTE: Debug Mode (not actually executing the commands)"

    if (op=="none") 
    {
	PrintError("you need to specify the operation you want to do");
	exit(1);
    }

    if (op=="exec") { Exec();   exit(0); }
    if (op=="move") { Move();   exit(0); }
    if (op=="link") { Link();   exit(0); }
    if (op=="rm")   { Remove(); exit(0); }
    if (op=="slink"){ Slink();  exit(0); }
    if (op=="exist"){ ExistCheck(); exit(0); }
    if (op=="graph"){ do { Graph(); } while (repeat); exit(0); }

    PrintError("you need to specify the operation you want to do");
    exit(1);

}' "$@"
exit $?
