2323# imports
2424import sys
2525import os
26+ import stat
2627import shutil
2728import subprocess
2829import fcntl
5051
5152WINDOW_MANAGERS = {
5253 "x11" : "W1" ,
53- "wayland " : "W2" ,
54+ "wayfire " : "W2" ,
5455 "labwc" : "W3" ,
5556}
5657
58+ FILE_MODES = {
59+ "+x" : stat .S_IXUSR | stat .S_IXGRP | stat .S_IXOTH ,
60+ "+r" : stat .S_IRUSR | stat .S_IRGRP | stat .S_IROTH ,
61+ "+w" : stat .S_IWUSR | stat .S_IWGRP | stat .S_IWOTH ,
62+ "a+x" : stat .S_IXUSR | stat .S_IXGRP | stat .S_IXOTH ,
63+ "a+r" : stat .S_IRUSR | stat .S_IRGRP | stat .S_IROTH ,
64+ "a+w" : stat .S_IWUSR | stat .S_IWGRP | stat .S_IWOTH ,
65+ "u+x" : stat .S_IXUSR ,
66+ "u+r" : stat .S_IRUSR ,
67+ "u+w" : stat .S_IWUSR ,
68+ "g+x" : stat .S_IXGRP ,
69+ "g+r" : stat .S_IRGRP ,
70+ "g+w" : stat .S_IWGRP ,
71+ "o+x" : stat .S_IXOTH ,
72+ "o+r" : stat .S_IROTH ,
73+ "o+w" : stat .S_IWOTH ,
74+ }
75+
5776
5877# pylint: disable=too-many-public-methods
5978class Shell :
@@ -142,6 +161,50 @@ def preexec():
142161 return False
143162 return True
144163
164+ def write_templated_file (self , output_path , template , ** kwargs ):
165+ """
166+ Use a template file and render it with the given context and write it to the specified path.
167+ The template file should contain placeholders in the format {key} which will be replaced
168+ with the corresponding values from the kwargs dictionary.
169+ """
170+ # if path is an existing directory, the template filename will be used
171+ output_path = self .path (output_path )
172+ if os .path .isdir (output_path ):
173+ output_path = os .path .join (output_path , os .path .basename (template ))
174+
175+ # Render the template with the provided context
176+ rendered_content = self .load_template (template , ** kwargs )
177+
178+ if rendered_content is None :
179+ self .error (
180+ f"Failed to load template '{ template } '. Unable to write file '{ output_path } '."
181+ )
182+ return False
183+
184+ append = kwargs .get ("append" , False )
185+ self .write_text_file (output_path , rendered_content , append = append )
186+
187+ return True
188+
189+ def load_template (self , template , ** kwargs ):
190+ """
191+ Load a template file and return its content with the placeholders replaced by the provided
192+ context. The template file should contain placeholders in the format {key} which will be
193+ replaced with the corresponding values from the kwargs dictionary.
194+ """
195+ if not os .path .exists (template ):
196+ self .error (f"Template file '{ template } ' does not exist" )
197+ return None
198+
199+ with open (template , "r" ) as template_file :
200+ template_content = template_file .read ()
201+
202+ # Render the template with the provided context
203+ for key , value in kwargs .items ():
204+ template_content = template_content .replace (f"{{{ key } }}" , str (value ))
205+
206+ return template_content
207+
145208 def info (self , message , ** kwargs ):
146209 """
147210 Display a message with the group in green
@@ -321,7 +384,10 @@ def reconfig(self, file, pattern, replacement):
321384 # Not found; append (silently)
322385 self .write_text_file (file , replacement , append = True )
323386
324- def pattern_search (self , location , pattern , multi_line = False , return_match = False ):
387+ # pylint: disable=too-many-arguments
388+ def pattern_search (
389+ self , location , pattern , multi_line = False , return_match = False , find_all = False
390+ ):
325391 """
326392 Similar to grep, but uses pure python
327393 multi_line will search the entire file as a large text glob,
@@ -331,16 +397,17 @@ def pattern_search(self, location, pattern, multi_line=False, return_match=False
331397 """
332398 location = self .path (location )
333399 found = False
400+ search_function = re .findall if find_all else re .search
334401
335402 if self .exists (location ) and not self .isdir (location ):
336403 if multi_line :
337404 with open (location , "r+" , encoding = "utf-8" ) as file :
338- match = re . search (pattern , file .read (), flags = re .DOTALL )
405+ match = search_function (pattern , file .read (), flags = re .DOTALL )
339406 if match :
340407 found = True
341408 else :
342409 for line in fileinput .FileInput (location ):
343- match = re . search (pattern , line )
410+ match = search_function (pattern , line )
344411 if match :
345412 found = True
346413 break
@@ -417,8 +484,13 @@ def chmod(self, location, mode):
417484 Change the permissions of a file or directory
418485 """
419486 location = self .path (location )
487+ # Convert a text mode to an integer mode
488+ if isinstance (mode , str ):
489+ if mode not in FILE_MODES :
490+ raise ValueError (f"Invalid mode string '{ mode } '" )
491+ mode = FILE_MODES [mode ]
420492 if not 0 <= mode <= 0o777 :
421- raise ValueError ("Invalid mode value" )
493+ raise ValueError (f "Invalid mode value ' { mode } ' " )
422494 if os .path .exists (location ):
423495 os .chmod (location , mode )
424496
@@ -475,6 +547,16 @@ def write_text_file(self, path, content, append=True):
475547 with open (self .path (path ), mode , encoding = "utf-8" ) as service_file :
476548 service_file .write (content )
477549
550+ def read_text_file (self , path ):
551+ """
552+ Read the contents of a file at the specified path
553+ """
554+ path = self .path (path )
555+ if not os .path .exists (path ):
556+ raise FileNotFoundError (f"File '{ path } ' does not exist" )
557+ with open (path , "r" , encoding = "utf-8" ) as file :
558+ return file .read ()
559+
478560 @staticmethod
479561 def is_python3 ():
480562 "Check if we are running Python 3 or later"
@@ -656,6 +738,29 @@ def set_window_manager(self, manager):
656738 ):
657739 raise RuntimeError ("Unable to change window manager" )
658740
741+ def get_window_manager (self ):
742+ """
743+ Get the current window manager
744+ """
745+ sessions = {"wayfire" : "LXDE-pi-wayfire" }
746+ # Check for Raspbian Desktop sessions
747+ if self .exists ("/usr/share/xsessions/rpd-x.desktop" ) or self .exists (
748+ "/usr/share/wayland-sessions/rpd-labwc.desktop"
749+ ):
750+ sessions .update ({"x11" : "rpd-x" , "labwc" : "rpd-labwc" })
751+ else :
752+ sessions .update ({"x11" : "LXDE-pi-x" , "labwc" : "LXDE-pi-labwc" })
753+
754+ matches = self .pattern_search (
755+ "/etc/lightdm/lightdm.conf" , "^(?!#.*?)user-session=(.+)" , False , True
756+ )
757+ if matches :
758+ session_match = matches .group (1 )
759+ for key , session in sessions .items ():
760+ if session_match == session :
761+ return key
762+ return None
763+
659764 def get_boot_config (self ):
660765 """
661766 Get the location of the boot config file
0 commit comments