#!/usr/bin/env python3
"""
April 25, 2025, Dr. Brian Fristensky, University of Manitoba
csh2sh.py - convert csh scripts to sh script.
            The input filename should be in the form
            x.csh, and the output file will be x.sh
 WARNING: This script only does some very crude 
 substitutions. Code generated by this script
 should be considered as a starting point for
 manual translation to sh.
 Version  11/15/09
 Synopsis:
  csh2sh.py infile 
       infile - csh script

@modified: May 26 2010
@author: Dale Hamel
@contact: umhameld@cc.umanitoba.ca
"""


import sys
import os
import re
import shutil
import sys


blib = os.environ.get("BIRCHPYLIB")
sys.path.append(blib)

from birchlib import Birchmod
from birchlib import Argument

PROGRAM = "csh2sh.py: "
USAGE = "\n\t USAGE: csh2sh.py infile {infile must be csh script}"
BM = Birchmod(PROGRAM, USAGE)


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class Parameters:
    "Wrapper class for command line parameters"
      	
    def __init__(self):
        """
     		Initializes arguments:
     			NFN=""
     			IFN=""
     			OFN=""
     		Then calls read_args() to fill in their values from command line
     		"""
        self.IFN = ""
        self.OFN = ""
        self.read_args()

    def read_args(self):
        """
     		Read command line arguments into a Parameter object
     		"""
   		
    		
   		
        infile = Argument("", str, BM)
        infile.set_position(1)
   		
        try:
            self.IFN = infile.fetch()
   		
            self.OFN = self.IFN
    		
            if self.OFN.endswith('.csh'):
                self.OFN = self.OFN[0:-4]
            self.OFN = self.OFN + '.sh'
   			
        except:
            BM.printusage()
   			
   		

    		

#    I = 2
#    while (I < numargs)  :
#          # This code is here as a placeholder for
#          # future command line switches
#	  if sys.argv[I] == "-dummy1" :
#             I = I + 1
#             if I < numargs :
#		I = I + 1
#	  elif sys.argv[I] == "-dummy2" :
#             I = I + 1
#             if I < numargs :
#		I = I + 1
#	  else :
#             P.OFN = sys.argv[I]
#             I = numargs	             	
  


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class scriptfile:
    "Contents of the file"

    def __init__(self):
        """
     	  Initializes arguments:
     		text=""
     	  """
        self.text = ""

    def read_file(self, IFN):
        """
          @param IFN: name of input file
          @type IFN: str
          """
        infile = open(IFN, 'r')
        self.text = infile.read()
        infile.close()
        return

    def write_file(self, OFN):
        """
          @param OFN: name of ouput file
          @type OFN:str
          """
        outfile = open(OFN, 'w')
        outfile.write(self.text)
        outfile.close()
        return

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def convert(script):
    """Iterate through the script replacing csh syntax with
    sh syntax.
    """
    script.text = re.sub('#!/bin/csh', '#!/bin/sh', script.text)
    
    # arguments
    script.text = re.sub('\$#argv', '$#', script.text)
    script.text = re.sub('\$argv', '$*', script.text)
    # We still need to handle the usage:  $argv[n]
    
    # Convert $home, $path to $HOME, $PATH
    script.text = re.sub('\$home', '$HOME', script.text)
    script.text = re.sub('\$path', '$PATH', script.text)

    #------ File name manipulations -----------

    # Basename: $FILE:t
    exp1 = '(\$)(.+)(:t)'
    exp2 = '`basename \g<1>\g<2>`'
    script.text = re.sub(exp1, exp2, script.text)
   
    # File extension: $FILE:e
    exp1 = '(\$)(.+)(:e)'
    exp2 = '\g<1>{\g<2>##*.}'
    script.text = re.sub(exp1, exp2, script.text)

    # Filename minus file extension: $FILE:r
    exp1 = '(\$)(.+)(:r)'
    exp2 = '\g<1>{\g<2>%.*}'
    script.text = re.sub(exp1, exp2, script.text)

    # ----------  Flow of control statements ---------------------
    
    # if then
    exp1 = '([\t ]*)(if[\t ]+)(.+)([\t ]+then)'
    exp2 = '\g<1>if [ \g<3> ]\n\g<1>\tthen'
    script.text = re.sub(exp1, exp2, script.text)
    exp1 = '(if \[)([\t ]+)(\()(.*)(\))([\t ]*)(\])'
    exp2 = 'if [ \g<4> ]'
    script.text = re.sub(exp1, exp2, script.text)
    script.text = re.sub('endif', 'fi', script.text)
    
    
    # set, setenv 
    exp1 = '(set[\t ]+)(\\S+)([\t ]+=[\t ]+)(\()(\\s)(.*)(\\s)(\))'
    exp2 = '\g<1>\g<2>\g<3>"\g<6>"'
    script.text = re.sub(exp1, exp2, script.text)
    script.text = re.sub('(set[\t ]+)(\\S+)([\t ]+=[\t ]+)', '\g<2>=', script.text)
    script.text = re.sub('([\t ]*)(setenv[\t ]+)(\\S+)([\t ]+)(\\S+)', '\g<1>\g<3>=\g<5>\n\g<1>export \g<3>', script.text)
       
    # switch
    script.text = re.sub('(switch[\t ]+\()(\\S+)([\t ]*\)[\t ]*)', 'case \g<2> in', script.text)
    script.text = re.sub('(case[\t ]+)(\\S+)([\t ]*\:[\t ]*)', '\g<2>)', script.text)
    script.text = re.sub('breaksw', ';;', script.text)
    script.text = re.sub('default:', '*)', script.text)
    script.text = re.sub('endsw', 'esac', script.text)
      
    # while
    exp1 = '(while[\t ]+)(\()(.*)(\)[\t ]*\n)'
    exp2 = 'while [ \g<3> ]\n\tdo\n'
    script.text = re.sub(exp1, exp2, script.text)
    script.text = re.sub('(\n[\t ]*)(end)', '\g<1>done', script.text)
    
    # foreach
    #this doesn't seem to work and I can't figure out why.
    exp1 = '([\t ]*)(foreach[\t ]+)(\\S+)([\t ]+)(.*)'
    exp2 = '\g<1>for \g<3> in \g<5>\n\g<1>    do\n'
    script.text = re.sub(exp1, exp2, script.text)
    # Also replace parentheses with double quotes, for lists in a for expression
    exp1 = '(for[\t ]+\\S+[\t ]+in[\t ])(\()(.*)(\))'
    exp2 = '\g<1>"\g<3>"'    
    script.text = re.sub(exp1, exp2, script.text)

    #Lists
    exp1 = re.compile('(\\S+=)(\()(.*?)(\))', re.DOTALL | re.MULTILINE)
    exp2 = '\g<1>"\g<3>"'
    script.text = re.sub(exp1, exp2, script.text, re.DOTALL | re.MULTILINE)

    # boolean tests
    exp1 = '(\$\{\?)(\\S+)(\})'
    exp2 = '! -z "$\g<2>"'
    script.text = re.sub(exp1, exp2, script.text)
      
    return    
        
#======================== MAIN PROCEDURE ==========================

def main():
    """
        Called when not in documentation mode.
        """
    P = Parameters ()
	
	
    # Read the csh file
    script = scriptfile()
    script.read_file(P.IFN)
	
    # Replace csh syntax with sh syntax.
    convert(script)
	
    # WARNING: this is currently a very simple-minded replacement.
    script.write_file(P.OFN)
    os.chmod(P.OFN, 0o755)
	
if (BM.documentor() or "-test" in sys.argv):
    pass
else:
    main()
