base.pyx
# Second 4.0 source code
#
# Filename: base.py
#
# This software is a product of Infinite, Inc., and was written by
# CPythonist (http://cpythonist.github.io/) of the development team of Infinite, Inc.
#
#
# Copyright 2024 Infinite Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Please report any bugs at using the email address in https://cpythonist.github.io/contact.html.
#
# Version declaration
__version__ = "4.0" # Defined as so as to be compatible for parse function of packaging.version module.
# It accepts only string, not integers and floats.
# Imports
from cmd import Cmd
from datetime import datetime as dt
from os import chdir, getcwd, getlogin, getpid, listdir, makedirs, mkdir, remove as removeFile, sep as osSeparator, scandir, startfile, system, walk
from os.path import basename, expanduser, getctime, getmtime, getsize, isdir, isfile, join, relpath
from pathlib import Path
from re import search
from shutil import copy2, copytree, rmtree, which as shutilWhich
from subprocess import run
from sys import exit as sysExit
import ctypes
import globalNamespace as gn
import printStrings
system('')
# Main class of the program
class Second4(Cmd):
def __init__(self):
super().__init__()
self.path = expanduser('~')
self.realPrompt = gn.PROMPT
def default(self, line): # Overwrite default method for parsing terminal and invalid commands
line = [i for i in line.split() if not (i == '' or i.isspace())]
gn.customPrint(f"&&SECOND4:## ??Error:## Unknown command: ??{line[0]}## -> {' '.join(line)}\n")
def emptyline(self): # Overwrite default method for emptyline
pass
def onecmd(self, line): # Used to make the program access the command when input in any case
"""
Used from cmd.py module of standard library of CPython 3.11.6.
Used for adding functionality of uppercase and mixed case commands.
"""
cmd, arg, line = self.parseline(line)
if not line:
return self.emptyline()
if cmd is None:
return self.default(line)
self.lastcmd = line
if line == 'EOF' :
self.lastcmd = ''
if cmd == '':
return self.default(line)
else:
try:
func = getattr(self, 'do_' + cmd.lower()) # Edited here. Added .lower() to variable cmd.
except AttributeError:
return self.default(line)
return func(arg)
def preloop(self): # Dynamic prompt to be updated once at the start of the program
self.prompt = gn.promptUpdater(self.path, self.realPrompt)
def postcmd(self, stop: bool, line: str) -> bool: # Dynamic prompt updated at the end of each loop
self.prompt = gn.promptUpdater(self.path, self.realPrompt)
return super().postcmd(stop, line)
def do_cd(self, args): # Change directory method
"""
Changes the current working directory.
Syntax:
CD path
^^path## Directory to change into.
"""
if (temp:=search('"(.*?)"', args)) or search("'(.*?)'", args): # Detecting quotation marks
args = [i for i in (args.split('"') if temp else args.split("'")) if not (i == '' or i.isspace())][0]
if (temp:=isdir(self.path + '\\' + args)) or isdir(args):
self.path = (str(Path(self.path + '\\' + args).resolve()) if temp else str(Path(args).resolve()))
gn.customPrint("&&CD:## !!Success:## Current working directory changed.\n")
else:
gn.customPrint("&&CD:## ??Error##: Directory not found.\n")
else: # If quotation marks are not used, use space as delimiters
args = [i for i in args.split() if not (i == '' or i.isspace())]
if len(args) == 1:
args = args[0]
if (temp1:=isdir(self.path + '\\' + args)) or isdir(args):
self.path = (str(Path(self.path + '\\' + args).resolve()) if temp1 else str(Path(args).resolve()))
gn.customPrint("&&CD:## !!Success:## Current working directory changed.\n")
else:
gn.customPrint(f"&&CD:## ??Error##: Directory \"{args}\" not found.\n")
else:
gn.customPrint("&&CD:## ??Error:## Too many parameters given.\n")
def do_cls(self, args): # Clear screen method
"""
Clears the output screen.
Syntax:
CLS
"""
system("cls")
print()
def do_command(self, args):
"""
Runs terminal commands on Second.
Originally, this feature was written in default method of this class, so that commands that
are not present in Second 4 could be executed as terminal commands. But this was later removed
to prevent accidental execution of terminal commands, and thus a new command was created in
Second 4 to accomodate the feature.
Syntax:
COMMAND command
^^command## -> Command to execute in terminal.
"""
gn.customPrint("&&COMMAND:## **Terminal output:##")
pathTemp = getcwd()
chdir(self.path)
system(args)
chdir(pathTemp)
gn.customPrint()
def do_copy(self, args): # File or directory copy method
"""
Copies a file/directory to another directory.
Syntax:
COPY source dest
source -> Path of source file/directory on the computer.
dest -> Destination directory for copying source into.
"""
if (temp:=search('"(.*?)\\s+"(.*?)"', args)) or search("'(.*?)'\\s+'(.*?)'", args): # Check for use of quotation marks
args = [i for i in (args.split('"') if temp else args.split("'")) if not (i == '' or i.isspace())]
src, dest = args
else: # If no quotation marks, use space as delimiters
args = args.split()
if len(args) == 2: # No argument support yet
src, dest = args
else:
gn.customPrint(f"&©:## ??Error##: Format of the command is incorrect. For help, please type **HELP COPY##.\n")
return None # Get out of loop ASAP!
if (temp1:=isfile(self.path + '\\' + src)) or isfile(src): # Is source a file?
src = str(Path(self.path + '\\' + src).resolve()) if temp1 else str(Path(src).resolve())
try:
if (temp2:=isdir(self.path + '\\' + dest)) or isdir(dest): # Check destination existance
dest = str(Path(self.path + '\\' + dest).resolve()) if temp2 else str(Path(dest).resolve())
if isfile(dest + '\\' + basename(src)): # If file already exists
gn.customPrint(f"&©:## **Info:## File \"{src}\" already exists. Do you want to overwrite it [y/n] (default y)? -> ", end='')
overwrite = input().lower() # Overwrite permission
if overwrite in ('', 'y', 'yes'):
copy2(src, dest)
gn.customPrint(f"&©:## !!Success##: File \"{src}\" overwrite was successful.\n")
elif overwrite in ('n', 'no'):
gn.customPrint(f"&©:## ?!Info:## File \"{src}\" was NOT copied.\n")
else: # File DOES NOT exist
copy2(src, dest)
gn.customPrint(f"&©:## !!Success##: File \"{src}\" copied successfully.\n")
else: # Oops, destination does not exist
gn.customPrint(f"&©:## ??Error##: Destination directory \"{str(Path(dest).resolve())}\" not found.\n")
except PermissionError: # No permission to write
gn.customPrint(f"&©:## ??Error##: Access is denied.\n")
except EOFError: # Process stopped in middle (like during overwrite input)
gn.customPrint(f"&©:## ??EOF:## Operation terminated.\n")
except Exception as e: # Any other unknown exceptions so that program does not crash
gn.customPrint("&©:## ??UnknownError##: " + str(e.__class__.__name__) + str(e))
elif (temp3:=isdir(self.path + '\\' + src)) or isdir(src): # Or is source a directory?
src = str(Path(self.path + '\\' + src).resolve()) if temp3 else str(Path(src).resolve())
try:
if (temp4:=(isdir(self.path + '\\' + dest)) or isdir(dest)): # Check directory existance
dest = str(Path(self.path + '\\' + dest).resolve()) if temp4 else str(Path(dest).resolve())
gn.customPrint(f"""&©:## **Info##: Destination directory "{str(Path(dest).resolve())}" exists.
&©:## **Input:## Do you want to copy into "{dest}" (some files can be overwritten) [y/n] (default y)? -> """, end='')
overwrite = input().lower() # Overwrite permission
if overwrite in ('', 'y', 'yes'):
copytree(src, dest, symlinks=True, dirs_exist_ok=True)
gn.customPrint(f"&©:## !!Success##: Directory copy was successful.\n")
elif overwrite in ('n', 'no'):
gn.customPrint(f"&©:## ?!Info##: Directory copy was NOT performed.\n")
else:
gn.customPrint(f"&©:## ??Info##: Invalid option. Directory copy was NOT performed.\n")
else:
try:
pathTemp = getcwd()
chdir(self.path)
makedirs(dest)
copytree(src, dest, symlinks=True, dirs_exist_ok=True)
gn.customPrint(f"&©:## !!Success##: Directory \"{str(Path(dest).resolve())}\" was created and copy from \"{src}\" was successful.\n")
except Exception as e:
gn.customPrint(f"&©:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
finally:
chdir(pathTemp)
except PermissionError:
gn.customPrint(f"&©:## ??Error##: Access is denied for source directory.\n")
except EOFError:
gn.customPrint(f"&©:## ??EOF:## Operation terminated.\n")
except Exception as e:
gn.customPrint(f"&©:## ??UnknownError##: {e.__class__.__name__}: {e}\n")
else:
gn.customPrint(f"&©:## ??Error##: Source file not found.\n")
def do_copyright(self, args):
"""
Displays the copyright information on Second.
Syntax:
COPYRIGHT
"""
gn.customPrint(printStrings.Base.copyright)
def do_credits(self, args):
"""
Displays the copyright information on Second.
Syntax:
COPYRIGHT
"""
gn.customPrint(printStrings.Base.credits)
def do_date(self, args):
"""
Displays the current system date.
Syntax:
DATE
"""
gn.customPrint(f"&&DATE:## **Info:## Date today: {dt.today().strftime('%d.%m.%Y (%d %B %Y)')} (dd.mm.yyyy).\n")
def do_del(self, args):
"""
Deletes a file/directory.
Syntax:
DEL path
path -> Path of file/directory to be deleted.
"""
if (temp:=search('"(.*?)"', args)) or search("'(.*?)'", args): # Manipulate the arguments given to interpret
args = [i for i in (args.split('"') if temp else args.split("'")) if not (i == '' or i.isspace())][0]
else:
if len(temp:=[i for i in args.split() if not (i == '' or i.isspace())]) == 1: # One argument present
args = temp[0]
else: # Oops! Many arguments present
gn.customPrint(f"&&DEL:## ??Error##: Format of the command is incorrect. For help, please type **HELP START##.\n")
return None # Get out of loop ASAP!
try:
pathTemp = getcwd() # I'm tired of writing comments for this particular thingy. It is present like four/five times.
# Kindly check the others, I have written the comments for one of them.
chdir(self.path)
if (temp:=isfile(args)) or isdir(args): # Check if file or directory, and store that data in temp
if temp:
removeFile(args)
gn.customPrint(f"&&DEL:## !!Success:## File \"{Path(args).resolve()}\" was successfully deleted.\n")
else:
rmtree(args)
gn.customPrint(f"&&DEL:## !!Success:## Directory \"{Path(args).resolve()}\" was successfully deleted.\n")
else: # File/Directory not found
gn.customPrint(f"&&DEL:## ??Error:## No file/directory named \"{args}\".\n")
except PermissionError:
gn.customPrint(f"&&DEL:## ??Error:## Atleast one file/directory in the tree is read-only or permissions unavailable for the operation.\n")
except Exception as e: # Unknown Exception occured
gn.customPrint(f"&&DEL:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
finally:
chdir(pathTemp) # Change to recorded directory
def do_dir(self, args):
"""
Displays the files and directories one level inside a directory.
Syntax:
DIR path
path -> Directory which needs to be examined.
"""
try:
if isdir(args): path = args
elif isdir(self.path + "\\" + args): path = self.path + "\\" + args
else: gn.customPrint(f"&&DIR:## ??Error:## No directory named \"{args}\".\n"); return None
gn.customPrint(f"&&DIR:## **Info:## Command DIR on directory \"{Path(path).resolve()}\".\n")
maxSize = 0
for j in scandir(path):
if len(str(j.stat().st_size)) > maxSize: maxSize = len(str(j.stat().st_size))
gn.customPrint(f"{'DATE CREATED':<19} {'DATE MODIFIED':<19} TYPE " + "{size:<{maximumSize}} NAME".format(size="SIZE", maximumSize=maxSize))
for i in scandir(path):
print(f"{dt.fromtimestamp(getctime(join(path, i.name))).strftime(r'%d-%m-%Y %H:%M:%S')} {dt.fromtimestamp(getmtime(join(path, i.name))).strftime(r'%d-%m-%Y %H:%M:%S')} {'FILE' if isfile(join(path, i.name)) else 'DIR '} " + "{size:<{maximumSize}} {name}".format(size=getsize(join(path, i.name)), maximumSize=maxSize, name=i.name))
gn.customPrint()
except PermissionError:
gn.customPrint("&&DIR:## ??Error:## Permissions unavailable for accessing the path given.\n")
def do_eof(self, args):
"""
Exits the program.
Syntax:
^Z (CTRL+Z)
"""
gn.customPrint(f"&&SECOND4:## !!Success:## Program second4.exe (PID:{getpid()}) exited successfully.\n{'-'*(65+len(str(getpid())))}\n")
sysExit(0)
def do_exit(self, args):
"""
Exits the program.
Syntax:
EXIT
"""
gn.customPrint(f"&&SECOND4:## !!Success:## Program second4.exe (PID:{getpid()}) exited successfully.\n{'-'*(65+len(str(getpid())))}\n")
sysExit(0)
def do_greet(self, args):
"""
Greets the user.
Syntax:
GREET [option]
option -> Specify option to greet the user.
1 - Greet option 1 (default)
2 - Greet option 2
"""
if (args in ('', '1')) or args.isspace(): # Default and mode 1
greetStr = f"Hello,"
elif args == '2': # Mode 2
time = int(dt.now().strftime("%H"))
if time in range(12):
greetStr = f"Good morning,"
elif time in range(12, 4):
greetStr = f"Good afternoon,"
elif time in range(4,24):
greetStr = f"Good evening,"
else: # That's mode 3, or INVALID!
greetStr = "??That's invalid syntax,##"
gn.customPrint(str(greetStr) + f" **{getlogin()}!##\n")
def do_help(self, args):
"""
Displays help menu.
Syntax:
HELP [command]
command -> Displays help for command with the name "command".
"""
if len(args:=[i for i in args.split() if not (i == '' or i.isspace())]) == 0: # No argument(s) specified, just print the thing
gn.customPrint(printStrings.Base.help, end='')
elif len(args) == 1: # Argument specified?
try:
temp = getattr(printStrings.Base, "help"+args[0].lower().capitalize()) # Try to get the value of the variable
gn.customPrint(f"&&HELP:## **{args[0].upper()}:##\n{temp}")
except AttributeError: # If not found (the variable)
gn.customPrint(f"&&HELP:## ??Error:## No command named \"{args[0]}\".\n")
else: # Oops! Too many arguments go against the rules!
gn.customPrint(f"&&HELP:## ??Error:## Too many arguments: {str(args)[1:-1]}.\n")
def do_mkdir(self, args):
"""
Creates a new directory.
Syntax:
MKDIR newdir
newdir -> Directory name for the new directory (relative or full path, or just directory name)
"""
pathTemp = getcwd() # Record current working directory (expected to be installation folder unless
# some bug/external interference had caused it to be something else)
chdir(self.path) # Change to the self.path directory (as os.mkdir() works with absolute and relative paths)
try:
mkdir(args) # Try to make new directory
gn.customPrint(f"&&MKDIR:## !!Success:## Directory \"{Path(args).resolve()}\" successfully created.\n")
except FileExistsError: # Directory exists
gn.customPrint(f"&&MKDIR:## ??Error:## Directory \"{Path(args).resolve()}\" already exists.\n")
except OSError: # Illegal character in directory name
gn.customPrint(f"&&MKDIR:## ??Error:## Invalid character in directory name \"{args}\".\n")
except Exception as e: # Unknown Exception occured
gn.customPrint(f"&&MKDIR:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
finally: # Always execute
chdir(pathTemp) # Change to the recorded directory
def do_prompt(self, args):
"""
Changes the prompt variable of the program.
Syntax:
PROMPT prompt
prompt -> New prompt for Second
%U - Username
%S - OS name
%R - Release number
%P - Path (current working directory)
%% - Percentage sign
Known bugs:
1. When the new prompt is given as one of the formatting symbols of the property of the font of the prompt
changes to the corresponding escape code (e.g., "!!" gives the prompt in green colour). The colour becomes
nomal after the program is restarted. Tried to resolve the issue but was unsuccessful.
"""
self.realPrompt = args # self.realprompt is used for storing the unformatted prompt for
# being dynamic (like current working directories)
if args == '': # If no arguments, restore original prompt of the program
self.realPrompt = f"{gn.BLUE}%U{gn.RESET}->{gn.BLUE}%S%R{gn.RESET}&&{gn.GREEN}%P{gn.RESET}(S4):~ {gn.YELLOW}${gn.RESET}"
gn.customPrint(f"&&PROMPT:## !!Success:## Original prompt variable restored.")
try:
f = open("settings.dat", 'rb+') # Open to load the data
data = gn.loadBin(f) # Load the data
f.close()
f = open("settings.dat", 'wb') # Overwrite the file
data["prompt"] = self.realPrompt # Update the data
gn.dumpBin(data, f) # Dump the data
f.close() # Close the file.
gn.customPrint(f"&&PROMPT:## !!Success:## Prompt variable successfully changed and stored as \"{self.realPrompt}\".\n")
except FileNotFoundError: # Settings.dat not found!
gn.customPrint("&&PROMPT:## ??Error:## File settings.dat was not found.\n")
gn.customPrint("&&PROMPT:## **Info:## Prompt variable will be temporarily changed. To resolve this issue, please restart the program.\n")
except (gn.UnpicklingError, KeyError): # Invalid data in settings.dat
gn.customPrint("&&PROMPT:## ??Error:## Empty/Invalid data in file settings.dat.")
gn.customPrint("&&PROMPT:## ??Info:## Prompt variable will be temporarily changed. To resolve this issue, please restart the program.\n")
except Exception as e: # Unknown Exception occured
gn.customPrint(f"&&PROMPT:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
def do_quit(self, args):
"""
Quits the program.
Syntax:
QUIT
"""
gn.customPrint(f"&&SECOND4:## !!Success:## Program second4.exe (PID:{getpid()}) exited successfully.\n{'-'*(65+len(str(getpid())))}\n")
sysExit(0)
def do_second(self, args):
"""
Displays the developer and operating system information of Second 4.
Syntax:
SECOND [-c]
-c -> Copies the command output to clipboard
"""
args = [i for i in args.split() if not (i == '' or i.isspace())] # Manipulate args to interpret
if not len(args): # Arguments not given
gn.customPrint(printStrings.Base.secondFormatted)
elif len(args) == 1: # If number of arguments is one and argument is 'c'
if args[0] == '-c':
gn.customPrint(printStrings.Base.secondFormatted)
run(["clip.exe"], input=printStrings.Base.secondUnformatted.encode('utf-8'), check=True)
gn.customPrint("&&SECOND:## !!Success:## Successfully copied output to clipboard.\n")
elif (args[0].replace('.', '', 1).isnumeric()) and (float(args[0]) == int(__version__.split('.')[0]) + 1):
gn.customPrint("&&SECOND5:## !!Don't know if I am released yet.## Check at http://cpythonist.github.io/second.html or http://github.com/cpythonist!\n")
else: # Else print error message
gn.customPrint(f"&&SECOND:## ??Error:## Unknown argument(s): {str(args)[1:-1]}.\n")
elif len(args) > 1: # Too many arguments given
gn.customPrint(f"&&SECOND:## ??Error:## Too many argument(s): {str(args)[1:-1]}.\n")
def do_shutdown(self, args):
"""
Shuts down the computer.
Syntax:
SHUTDOWN [options]
options -> Customise options for shutdown.
-s - Option for shutdown.
-r - Option for restart.
-t - Sets countdown for SHUTDOWN operation.
-h - Enables hybrid mode while startup.
If only '-h' and/or '-t' options are used, then by default '-s' argument will be executed.
"""
args = [i for i in args.split() if not (i == '' or i.isspace())]
command = ''
for i in args:
if i == '-s':
command += " /s"
elif i == '-r':
command += " /r"
elif i.startswith('-t') and i[1:].isnumeric():
command += f" /t {int(i[1:]):03d}"
elif i == '-h':
command += " /hybrid"
else:
gn.customPrint(f"&&SHUTDOWN:## ??Error:## Unknown argument: {i}\n")
if ('-s' not in command) and ('-r' not in command):
command += " /s"
try:
gn.customPrint("&&SHUTDOWN:## !!Success:## Shutdown command activated.\n")
system("shutdown" + command)
except Exception as e:
gn.customPrint(f"&&SHUTDOWN:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
def do_start(self, args):
"""
Starts a file, directory or executable.
Syntax:
START name [-admin]
name -> Relative/Full path of file/directory, or name of program present in the PATH variable.
-admin -> Run as Administrator.
"""
isAdminMode = False
if (temp:=search('"(.*?)"(.*?)', args)) or search("'(.*?)'(.*?)", args):
args = (temp1:=[i.strip() for i in (args.split('"') if temp else args.split("'")) if not (i == '' or i.isspace())])[0]
optArgs = temp1[1:]
if len(optArgs) == 0: pass
elif len(optArgs) == 1:
if optArgs[0] == "-admin":
isAdminMode = True
else:
gn.customPrint(f"&&START:## ??Error:## Invalid argument: '{optArgs[0]}'.\n")
return None
else:
gn.customPrint(f"&&START:## ??Error##: Too many arguments {', '.join(i for i in optArgs)}.\n")
return None
else:
if len(temp:=[i.strip() for i in args.split() if not (i == '' or i.isspace())]):
args = temp[0]
optArgs = temp[1:]
if len(optArgs) == 0: pass
elif len(optArgs) == 1:
if optArgs[0] == "-admin":
isAdminMode = True
else:
gn.customPrint(f"&&START:## ??Error:## Invalid argument: '{optArgs[0]}'.\n")
return None
else:
gn.customPrint(f"&&START:## ??Error##: Too many arguments: {temp}.\n")
return None
else:
gn.customPrint(f"&&START:## ??Error##: Format of the command is incorrect. For help, please type **HELP START##.\n")
return None # Get out of loop ASAP!
try:
pathTemp = getcwd() # There is exactly the same block of code in do_mkdir, I guess. Please see that documentation.
chdir(self.path)
if isfile(args) or isdir(args):
startfile(args, 'runas') if isAdminMode else startfile(args)
gn.customPrint(f"&&START:## !!Success:## File/Directory \"{Path(args).resolve()}\" opened successfully.\n")
else:
startfile(args, 'runas') if isAdminMode else startfile(args)
gn.customPrint(f"&&START:## !!Success:## Executable \"{shutilWhich(args)}\" opened successfully {'with administrator previlages' if isAdminMode else ''}.\n")
except FileNotFoundError as e:
gn.customPrint(f"&&START:## ??Error:## \"{args}\" is not a file, directory or executable, or the file is not accessible as requested.\n")
except OSError as e:
gn.customPrint("&&START:## ??Error:## The process was aborted by the user, or the file is not accessible by the Administrator account.\n")
except Exception as e:
gn.customPrint(f"&&START:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
finally:
chdir(pathTemp)
def do_time(self, args):
"""
Displays the current system time.
Syntax:
TIME
"""
gn.customPrint(f"&&TIME:## **Info:## Time now is: {dt.now().strftime('%H:%M.%S [%f] (%d %B %Y)')} (hh:mm.ss [microseconds]).\n")
def do_title(self, args):
"""
Changes the title of the console window.
Syntax:
TITLE title
title -> New title for the Second window.
"""
try:
ctypes.windll.kernel32.SetConsoleTitleW(args) # Try to change the title
gn.customPrint(f"&&TITLE:## !!Success:## Title successfully changed to \"{args}\".\n")
except Exception as e: # If any error (I've never see any), then print it.
gn.customPrint(f"&&TITLE:## ??UnknownError:## {e.__class__.__name__}: {e}\n")
def do_tree(self, args):
"""
Displays a tree of all files and subdirectories inside a directory.
Syntax:
TREE dir
dir -> Directory which needs to be examined.
"""
if (isdir(args)): treePath = args
elif (isdir(self.path + "\\" + args)): treePath = self.path + "\\" + args
else: gn.customPrint(f"&&TREE:## ??Error:## Path \"{args}\" not found.\n"); return None
inDirChars = "⁞﹍﹍﹍"
count = 0
gn.customPrint(f"&&TREE: **Info:## Command TREE run on directory \"{Path(treePath).resolve()}\".\n")
gn.customPrint(str(Path(treePath).resolve()))
for root, dirs, files in walk(treePath):
if count == 0: count += 1
else:
print((len(relpath(root, treePath).split(osSeparator))-1)*' ' + inDirChars + basename(root))
for file in files:
print((len(relpath(root, treePath).split(osSeparator)))*' ' + inDirChars + file)
gn.customPrint()
globalNamespace.pyx
# Second 4.0 source code
#
# Filename: globalNamespace.py
#
# This software is a product of Infinite, Inc., and was written by
# CPythonist (cpythonist.github.io) of the development team of Infinite, Inc.
#
#
# Copyright 2024 Infinite Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Please refer https://cpythonist.github.io/second/documentation/secondDoc4.0.html
# for documentation.
# Please report any bugs using the email address in https://cpythonist.github.io/contact.html.
#
# Imports
from getpass import getuser
from pickle import UnpicklingError, load as loadBin, dump as dumpBin
from platform import system as platSys, release as platRel, version as platVer
from re import sub as substitute
from sys import stdout
# Declaration of escape codes for text-formatting
BOLD = "\033[1m"
BLINK = "\033[5m"
BLUE = "\033[94m"
CLS = "\033[H\033[J"
CYAN = "\033[96m"
GREEN = "\033[92m"
HEADER = "\033[95m"
RED = "\033[91m"
RESET = "\033[0m"
UNDERLINE = "\033[4m"
YELLOW = "\033[93m"
def customPrint(*string, end='\n', sep=' ') -> None:
"""
Function: customPrint
Custom function for output to sys.stdout with easy text-formatting.
Uses regular expressions to evaluate strings given and replace characters
as necessary with formatting data.
Formatting options:
?? -> Red
?! -> Yellow
!! -> Green
** -> Cyan
^^ -> Blue
&& -> Bold
__ -> Underline
## -> Reset
Returns None.
"""
finalStr = []
replace = (
("\\?\\?", RED),
("\\?!", YELLOW),
("!!", GREEN),
("\\*\\*", CYAN),
("\\^\\^", BLUE),
("\\&\\&", BOLD),
("__", UNDERLINE),
("\\#\\#", RESET)
)
for i in string:
for pattern, repl in replace:
i = substitute(pattern, repl, i)
finalStr.append(i)
stdout.write(sep.join(finalStr) + end) # sys.stdout.write() does not support giving strings as a list/tuple of parameters
def readSettings() -> None:
"""
Function to read settings from file settings.dat.
If file is found and data is correct, settings are loaded into global variables for later use.
Else if file is:
1. Empty: Attempts to write default values into file.
2. Invalid: Attempts to erase the file and write default values into file.
3. Not found: Attempts to create file and write default values into file.
Returns None.
"""
global PROMPT # Global prompt variable
isDataLoaded = False # For checking if settings has been read in one of the conditions
try:
customPrint("Loading settings... ")
f = open("settings.dat", 'rb+')
data = loadBin(f) # Data is stored in dictionary
isDataLoaded = True
PROMPT = data["prompt"]
except EOFError:
if isDataLoaded: # Check if the data was loaded (for checking empty settings.dat file)
f.close()
customPrint("Done")
else: # Try to correct file settings.dat
f.truncate(0)
customPrint("?!File settings.dat was empty.## Writing default values... ", end='')
try:
with open("settings.dat", 'wb') as f: # Opening with 'wb' mode even though 'rb+' is perfectly valid
# as file is going to empty but will exist
dumpBin({"prompt":f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"}, f)
except PermissionError: # Catch permission denies
customPrint("?!Permissions for writing data in file settings.dat not available.## ", end='')
PROMPT = f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"
customPrint("Loading default settings...")
except FileNotFoundError: # File settings.dat not found
customPrint("?!The file settings.dat does not exist.## Creating new file... ", end='')
try:
with open("settings.dat", 'wb') as f: # Try to create new settings.dat file
customPrint("Writing default values...")
dumpBin({"prompt":f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"}, f)
except PermissionError: # Permission denied. Ignore creating file. Maybe on next run the issue can be resolved
customPrint("?!Permissions for creating/writing data in file settings.dat not available.##")
PROMPT = f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"
customPrint("Loading default settings...")
except (UnpicklingError, KeyError):
customPrint("?!Invalid data in file settings.dat.##", end='')
choice = input("""Do you want to erase the file and write the default values? (default) [y/n]
""").lower()
if choice in ('', 'y', 'yes'):
try:
with open("settings.dat", 'wb') as f: # Try to erase file by using 'w' mode and write default values
customPrint("Writing default values... ", end='')
dumpBin({"prompt":f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"}, f)
except PermissionError:
customPrint("?!Permissions for writing data in file settings.dat not available.## ", end='')
PROMPT = f"{BLUE}%U{RESET}->{BLUE}%S%R{RESET}&&{GREEN}%P{RESET}(S4):~ {YELLOW}${RESET}"
customPrint("Loading default settings...")
else:
print("?!Invalid option entered.## File will be left as it is. Loading default values...")
except Exception as e: # Data not readable in file settings.dat
customPrint(f"&&SECOND4:## ??UnknownError:## {e.__class__.__name__}: {e}")
def promptUpdater(path, prompt):
skip = 0 # skip is used for boosting performance
while bool([i for i in ("%%", "%P", "%U", "%S", "%R") if i in prompt.upper()]): # Check if '%' is in string for speed
try:
for i in range(skip, len(prompt)+1): # I simply don't understand why it needs to be len(prompt)+1 when it can be
# len(prompt)-1 or atleast len(prompt). I tried this in frustration during
# debugging and it worked. If someone knows how, please try to mail me why.
if prompt[i] == '%': # Check character for placeholder part
if prompt[i+1] in "UuSsRrPp%": # Valid characters following '%' to be a placeholder
# %(Pp) -> Path (current working directory)
# %(Rr) -> Release number
# %(Ss) -> OS name
# %(Uu) -> Username
# %(%) -> %
if prompt[i+1] in "Uu": prompt = prompt[:i] + getuser() + prompt[i+2:]; skip += len(getuser())
elif prompt[i+1] in "Ss": prompt = prompt[:i] + platSys() + prompt[i+2:]; skip += len(platSys())
elif prompt[i+1] in "Rr":
ver = platVer(); rel = platRel()
if rel == "10": rel = "11" if (int(ver.split('.')[2]) > 22000) else "10"
prompt = prompt[:i] + rel + prompt[i+2:]; skip += len(rel)
elif prompt[i+1] in "Pp": prompt = prompt[:i] + path + prompt[i+2:]; skip += len(path)
elif prompt[i+1] == '%': prompt = prompt[:i+1] + prompt[i+2:]; skip += 1
break
except IndexError as e:
break
return prompt
printStrings.pyx
# Second 4.0 source code
#
# Filename: printStrings.py
#
# This software is a product of Infinite, Inc., and was written by
# CPythonist (cpythonist.github.io) of the development team of Infinite, Inc.
#
#
# Copyright 2024 Infinite Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Please refer https://cpythonist.github.io/second/documentation/secondDoc4.0.html
# for documentation.
# Please report any bugs using the email address in https://cpythonist.github.io/contact.html.
#
def getHelpString(helpStr: dict): # Forms the print string for HELP.
line = f"**__COMMAND##** __FUNCTION##"
count = 0
for key in helpStr:
if not count%3:
line += f"\n{key:<15}{helpStr[key]}"
elif count%3 == 1:
line += f"\n!!{key:<15}##!!{helpStr[key]}##"
elif count%3 == 2:
line += f"\n**{key:<15}##**{helpStr[key]}##"
count += 1
return line
class Base: # Contains print strings for base.py
helpStrings = {
"&&CD": "Changes the current working direcory (CWD).",
"&&CLS": "Clears the screen.",
"&&COMMAND": "Executes a terminal command.",
"&©": "Copies file/directory to another directory",
"&©RIGHT": "Displays the copyright information of Second.",
"&&CREDITS": "Displays the credits information of Second.",
"&&DATE": "Displays today's date.",
"&&DEL": "Deletes a file/directory.",
"&&DIR": "Displays the files and directories one level inside a directory.",
"&&EXIT": "Terminates Second.",
"&&GREET": "Greets the user.",
"&&HELP": "Displays this help menu and help outputs of other commands.",
"&&MKDIR": "Creates a new directory.",
"&&PROMPT": "Changes the prompt variable.",
"&&QUIT": "Quits Second.",
"&&SECOND": "Displays version compactibility and author.",
"&&START": "Opens a file/directory.",
"&&TIME": "Displays current time.",
"&&TITLE": "Changes the title of the window.",
"&&TREE": "Displays a tree of all files and subdirectories inside a directory."
}
help = f"""&&HELP:## **Info:##
{getHelpString(helpStrings)}
&&**1.## In case a terminal command and Second command names clash, the Second command is given preference and executed.
&&**2.## Commands are case-insensitive.
"""
secondFormatted = """&&SECOND:## **Info:##
&&**Second 4.0##
Developed by Infinite, Inc.
Developer: CPythonist (cpythonist.github.io)
License: Apache-2.0
CPython version used for development: &&!!3.11.6##
Cython version used for optimisation: &&!!3.0.6##
Nuitka version used for compilation to binary: &&!!2.0.6##
Inno Setup version used for installer archive: &&!!6.2.2##
Operating system: &&**Windows##
Windows version: &&**10, 11##
"""
secondUnformatted = """Second 4.0
Developed by Infinite, Inc.
Developer: CPythonist (cpythonist.github.io)
License: Apache-2.0
CPython version used for development: 3.11.6
Cython version used for optimisation: 3.0.6
Nuitka version used for compilation to binary: 2.0.6
Inno Setup version used for installer archive: 6.2.2
Operating system: Windows
Windows version: 10, 11
"""
helpCd = """
!!Changes the current working directory.##
&&__Syntax:##
CD path
^^path## Directory to change into.
"""
helpCls = """
!!Clears the output screen.##
&&__Syntax:##
CLS
"""
helpCommand = """
!!Runs terminal commands on Second.##
&&__Syntax:##
COMMAND command
^^command## -> Command to execute in terminal.
"""
helpCopy = """
!!Copies a file/directory to another directory.##
&&__Syntax:##
COPY source dest
^^source## -> Path of source file/directory on the computer.
^^dest## -> Destination directory for copying source into.
"""
helpCopyright = """
!!Displays the copyright information on Second.##
&&__Syntax:##
COPYRIGHT
"""
helpCredits = """
!!Displays the credits information on Second.##
&&__Syntax:##
CREDITS
"""
helpDate = """
!!Displays the current system date.##
&&__Syntax:##
DATE
"""
helpDel = """
!!Deletes a file/directory.##
&&__Syntax:##
DEL path
^^path## -> Path of file/directory to be deleted.
"""
helpDir = """
!!Displays the files and directories one level inside a directory.##
&&__Syntax:##
DIR dir
^^dir## -> Directory which needs to be examined.
"""
helpEof = """
!!Exits the program.##
&&__Syntax:##
^Z (CTRL+Z)
"""
helpExit = """
!!Exits the program.##
&&__Syntax:##
EXIT
"""
helpGreet = """
!!Greets the user.##
&&__Syntax:##
GREET [option]
^^option## -> Specify option to greet the user.
1 - Greet option 1 (default)
2 - Greet option 2
"""
helpHelp = """
!!Displays help menu.##
&&__Syntax:##
HELP [command]
^^command## -> Displays help for command with the name "command".
"""
helpMkdir = """
!!Creates a new directory.##
&&__Syntax:##
MKDIR newdir
^^newdir## -> Directory name for the new directory (relative or full path, or just directory name)
"""
helpPrompt = """
!!Changes the prompt variable of the program.##
&&__Syntax:##
PROMPT prompt
^^prompt## -> New prompt for Second
%U - Username
%S - OS name
%R - Release number
%P - Path (current working directory)
%% - Percentage sign
"""
helpQuit = """
!!Quits the program.##
&&__Syntax:##
QUIT
"""
helpSecond = """
!!Displays the developer and operating system information of Second 4.##
&&__Syntax:##
SECOND [-c]
^^-c## -> Copies the command output to clipboard
"""
helpShutdown = """
!!Shuts down the computer.##
&&__Syntax:##
SHUTDOWN [options]
^^options## -> Customise options for shutdown.
-s - Option for shutdown.
-r - Option for restart.
-t - Sets countdown for SHUTDOWN operation.
-h - Enables hybrid mode while startup.
If only '-h' and/or '-t' options are used, then by default '-s' argument will be executed.
"""
helpStart = """
!!Starts a file, directory or executable.##
&&__Syntax:##
START name [-admin]
^^name## -> Relative/Full path of file/directory, or name of program present in the PATH variable.
^^-admin## -> Run as Administrator.
"""
helpTime = """
!!Displays the current system time.##
&&__Syntax:##
TIME
"""
helpTitle = """
!!Changes the title of the console window.##
&&__Syntax:##
TITLE title
^^title## -> New title for the Second window.
"""
helpTree = """
!!Displays a tree of all files and subdirectories inside a directory.##
&&__Syntax:##
TREE dir
^^dir## -> Directory which needs to be examined.
"""
copyright = """&&SECOND:## **Copyright:##
Copyright (c) 2024 Infinite Inc.
Written by Cpythonist (http://cpythonist.github.io/)
All rights reserved.
"""
credits = """&&SECOND:## **Credits:##
Thanks to all the authors and contributors of the programming language and libraries used in this program:
CPython: ^^http://python.org/##
psutil: ^^http://github.com/giampaolo/##
requests: ^^http://github.com/psf/##
Nuitka: ^^http://github.com/Nuitka##
Cython: ^^http://github.com/cython/##
Inno Setup: ^^http://github.com/jrsoftware/##
"""
second4.py
# Second 4.0 source code
#
# Filename: second4.py
#
# This software is a product of Infinite, Inc., and was written by
# CPythonist (http://cpythonist.github.io/) of the development team of Infinite, Inc.
#
#
# Copyright 2024 Infinite Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Improvements aimed for Second 4.0:
# 1. Added commands from previous versions (Second 1 and 2)
# 2. Added dark mode support (extremely unstable and under development).**
# 3. Higher efficiency using the cmd module of the standard library of
# CPython-3.11.6 for greater performance.
# 4. Consoles without ANSI support can also run the program.**
# 5. Easy usage by removing the necessity of quotes for all arguments.
# 6. Add update feature.
# 7. Add functionality to execute terminal commands from Second.
# ** Implementation removed.
#
# Please refer https://cpythonist.github.io/second/documentation/secondDoc4.0.html
# for documentation.
# Please report any bugs at the email address in https://cpythonist.github.io/contact.html.
#
# Please note that in settings.dat, the DARKMODE (dictionary key "darkmode": (True/False/"auto") variable
# has not been removed. This is left as such as future versions may include a (hopefully) fully-functional
# colour mode support.
#
def main():
# The "try-wrap"
try:
# Imports
# Imports are declared inside try statement only in this program so as to catch any Exception and
# log it into error files. This is the program second4.exe and thus any error needs to be logged.
from logging import DEBUG, getLogger, FileHandler, Formatter
from os import makedirs, getpid
from os.path import isdir
from subprocess import Popen, PIPE
from sys import exit as sysExit, argv
from traceback import format_exc as formatExcep
import base
import ctypes, globalNamespace as gn
# Initialisation for use of escape codes
base.system('')
gn.readSettings() # Read user settings from settings.dat
ctypes.windll.kernel32.SetConsoleTitleW("Second 4") # Set title of console window
prog = base.Second4() # Create object prog from Second4
gn.customPrint(f"""
&&**Infinite Second {base.__version__}##
Written by CPythonist (http://cpythonist.github.io/)
!!Developed in CPython 3.11.6
Optimised using Cython 3.0.6
Compiled using Nuitka 1.9.2
Compressed to installer executable using Inno Setup 6.2.2
Thanks to __stackoverflow.com## !!for the necessary help!##
Type "help" without the quotes for the help menu.
""")
# Run the updater as a separate process. Given parameters are the process ID of self process to be passed to installer.
# Popen(["second4Updater", str(getpid())], executable=argv[0], stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True, shell=False)
# Didn't use this as the problem of FileNotFoundError couldn't be resolved (the program couldn't identify the updater and update
# installer files).
while True: # KeyboardInterrupts to be caught in cmd module was either not possible or was difficult to implement, OR IDK how to implement
try:
prog.cmdloop()
prog.prompt = gn.promptUpdater(prog.path, prog.realPrompt)
except FileNotFoundError:
gn.customPrint(f"&&SECOND4:## ??CriticalError:## The current working directory \"{prog.path}\" does not exist. Attempting to change to parent directory...")
exist = False
temp = prog.path
while not exist:
if isdir(temp:='\\'.join(temp.split('\\')[:-1])):
prog.path = temp; exist = True
gn.customPrint(f"&&SECOND4:## **Info:## The parent directory \"{temp}\" exists and the current working directory has been changed to it.\n")
else: gn.customPrint(f"&&SECOND4:## ??CriticalError:## The parent directory \"{temp}\" does not exist. Trying for the next parent directory...")
except KeyboardInterrupt as e:
print()
except Exception as e: # Handle fatal Exceptions and log the error output.
gn.customPrint(f"&&SECOND4:## ??UnknownError:## {e.__class__.__name__}: {e}\n", end='')
makedirs("errOut", mode=0o777, exist_ok=True)
handler = FileHandler(filename="errOut\\mainErrLog.log", mode="a")
handler.setLevel(DEBUG)
handler.setFormatter(Formatter("\n%(asctime)s\n%(levelname)s: %(name)s: %(message)s"))
logger = getLogger("main")
logger.addHandler(handler)
logger.fatal(f"Fatal Exception:\n{formatExcep()}")
gn.customPrint("-" * (25 + len(f"{e.__class__.__name__}{e}")) + "\n")
sysExit(1)
if __name__ == "__main__":
main()
setupCython.py
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("*.pyx")
)