Re: [Sawfish] possible infinite recursion? |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/sawfish Archives
]
- To: sawfish@xxxxxxxxxxxxxxxxxxx
- Subject: Re: [Sawfish] possible infinite recursion?
- From: Teika Kazura <teika@xxxxxxxxxxx>
- Date: Fri, 12 Aug 2011 07:32:14 +0900 (JST)
- Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=lavabit; d=lavabit.com; b=b+GPujCcNrFOYuGu6Du/BJMgiAdb22/rda+CRT26HYrC4YRwJ77ev7sEAX06YZhuRdRoeKuCIFgT0VGcswxfIlkYYm7LcXca54ol6QIpZpwBHzQ4HTt0rHwRoW5+i4u6BQBiUocps9G92yEr9tnX3oCsV7Oia7i+VvGRuiSZJzE=; h=Date:Message-Id:To:Subject:From:In-Reply-To:References:Mime-Version:Content-Type:Content-Transfer-Encoding;
Hi, Herbert Poetzl, welcome back to Sawfish!
On Fri, 5 Aug 2011 19:57:36 +0200, Herbert Poetzl wrote:
> a second test run with --interp gave the somewhat
> more informative trace which can be found here:
>
> http://vserver.13thfloor.at/Stuff/sawfish-1.8.1.trace
Sorry, forget it. The max recursion depth is too narrow, and with
--interp, tail recursions (ignore it if you don't know it) are not
done, and the recurrsion gets far deeper. So f-spot is not the
culprit.
Please save the attached file at
~/.sawfish/lisp/sawfish/wm/ext/apps-menu.jl, change dir there,
run:
sawfish --batch -l compiler -f compile-batch apps-menu.jl
and restart sawfish again. It'll print the names of *.desktop it's
processing. Please send us the last *.desktop.
(If you're done, delete the files in that directory.)
> the problem went away when I created a ~/.sawfish/rc
> probably because the app menu building code wasn't
> executed anymore (tx oGMo)
Hmm. It's not directly related the true problem, but something other
is wrong, too. The apps-menu is created unless you do
(setq apps-menu-autogen nil).
Regards,
Teika (Teika kazura) - A retired dev of Sawfish.
;; apps-menu.jl -- generate applications menu from *.desktop files
;; (c) 2009 - 2011 Matthew Love
;; This file is part of sawfish.
;; sawfish is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; sawfish is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with sawfish; see the file COPYING. If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Description:
;;
;; Generate applications menu from *.desktop files in the directory
;; /usr/share/applications .
;; "Desktop entry specification", *.desktop files spec, is defined in:
;; http://standards.freedesktop.org/desktop-entry-spec/latest/
;; 'fdo' in some names stands for "freedesktop.org".
;;; Todo:
;;; Notes: we don't handle non-utf8 encoding.
;;; Code:
(define-structure sawfish.wm.ext.apps-menu
(export generate-apps-menu
init-apps-menu
update-apps-menu
parse-fdo-file
fdo-filter-record
fdo-toplevel-filter
fdo-nodisplay-filter
fdo-hidden-filter
fdo-onlyshowin-filter
fdo-notshowin-filter
fdo-default-filter
fdo-some-filter)
(open rep
rep.io.files
rep.io.streams
rep.system
rep.regexp
sawfish.wm
sawfish.wm.menus
sawfish.wm.commands
sawfish.wm.commands.launcher)
(define-structure-alias apps-menu sawfish.wm.ext.apps-menu)
;; User Options
(defvar apps-menu-autogen t
"If non-nil, `apps-menu' is automatically generated from `user-apps-menu'
and *.desktop files. If you set `apps-menu', then it won't happen anyway.")
(defvar user-apps-menu '()
"Your own applications menu entries. It is followed by auto generated
applications menu.")
(defvar apps-menu-filter 'default
"The filter to use while generating the `apps-menu'. The default filters
include `fdo-toplevel-filter' `fdo-nodisplay-filter' `fdo-hidden-filter'
`fdo-onlyshowin-filter' and `fdo-notshowin-filter'. Can also be set with
'default or 'some, both of which are combinations of the default filters,
'default uses them all and 'some only uses `fdo-notshowin-filter' and
`fdo-onlyshowin-filter'. This can be set to 'nil or '() to perform no
filtering on the `apps-menu'.")
(defvar apps-menu-associate-categories t
"Associate desktop entry categories with the category-master-list")
(defvar desktop-directory '("/usr/share/applications")
"List of directories to look for *.desktop files.")
(defvar apps-menu-alphabetize t
"Sort the apps menu alphabetically.")
(defvar apps-menu-lang nil
"Human language for applications menu, in string. Default is set from locale.")
;; The Master Category List
(defvar desktop-cat-alist
'(("Top-Level" . ("Application" "Applications" "GNOME" "KDE" "X-Xfce-Toplevel"
"GTK" "Qt"))
("Desktop" . ("X-Desktop" "X-DesktopApplets" "X-DesktopCountry"))
("Office" . ("Office" "WordProcessor" "Presentation" "X-Document"
"TextEditor" "SpreadSheet" "Calculator" "X-Calculate"
"Chart" "FlowChart" "Finance" "Calendar" "ContactManagement"
"X-Personal" "X-PersonalUtility" "Dictionary"))
("Internet" . ("Telephony" "Network" "Dialup" "VideoConference"
"RemoteAccess" "News" "HamRadio" "FileTransfer"
"X-Internet" "P2P" "Email" "WebBrowser" "IRCClient"
"Chat" "InstantMessaging" "Chat" "WebDevelopment"))
("Games" . ("Game" "ActionGame" "AdventureGame" "ArcadeGame"
"BoardGame" "BlocksGame" "CardGame" "KidsGame"
"LogicGame" "RolePlaying"))
("Graphics" . ("RasterGraphics" "VectorGraphics" "X-GraphicUtility"
"2DGraphics" "3dGraphics" "3DGraphics" "Scanning"
"OCR" "Photography" "Viewer" "Publishing" "Art"
"ImageProcessing"))
("Media" . ("AudioVideo" "Audio", "Video" "Midi" "Mixer" "Sequencer"
"Tuner" "TV" "AudioVideoEditing" "Player" "Recorder"
"DiscBurning" "Music"))
("Science" . ("Science" "Astrology" "ArtificialIntelligence"
"Astronomy" "Biology" "Chemistry" "ComputerScience"
"DataVisualization" "Electricity" "Robotics" "Physics"
"Math" "Education" "Geography" "Simulation"))
("Development" . ("GUIDesigner" "IDE" "Profiling" "RevisionControl"
"ProjectManagement" "Translation" "Java"
"Development" "Documentation" "Editors"))
("Utility" . ("X-SystemMemory" "Utility" "X-SetupEntry"
"X-SetupUtility" "X-SystemMemory" "TextTools"
"TelephonyTools" "Accessibility" "Clock" "ConsoleOnly"))
("Filesystem" . ("X-FileSystemFind" "X-FileSystemUtility" "Archiving"
"FileManager" "X-FileSystemMount" "Compression"))
("System" . ("X-SystemSchedule" "System" "X-SystemMemory" "Emulator"
"TerminalEmulator" "Printing" "Monitor" "Security"))
("Settings" . ("Settings" "HardwareSettings" "PackageManager"
"X-GNOME-PersonalSettings" "DesktopSettings"))
("Exiles" . ("Exile"))))
(define this-line nil)
(define name-string "Name[")
;; fdo-file-parsing
(define (fdo-skip-line-p instring)
"Return `t' if `instring' should be skipped."
(or (eq (aref instring 0) ?#)
(eq (aref instring 0) ?\n)))
(define (check-if-desktop-stream instream)
"Check for the `[Desktop Entry]' line in `instream'"
(let ((line (read-line instream)))
(when line
(if (string= line "[Desktop Entry]\n")
't
(when (fdo-skip-line-p line)
(check-if-desktop-stream instream))))))
(define (desktop-file-p directory-file)
"Quickly check if `directory-file' is a `*.desktop' file."
(condition-case nil
(let ((this-file (open-file directory-file 'read)))
(check-if-desktop-stream this-file))
;; unreadable -> return nil
(file-error)))
(define (get-key-value-pair instring)
"Split a `*.desktop' file line into its key-value pair.
Returns (key . value)"
;; Sorry, \\s doesn't work. Why??
(if (string-match "^([^ \t=]+)[ \t]*=[ \t]*([^\n]+)" instring)
(cons (expand-last-match "\\1") (expand-last-match "\\2"))
;; Ususally, it doesn't reach here.
(cons "" "")))
(define (fdo-group-p instring)
(eq (aref instring 0) ?\[))
(define (get-fdo-group instring)
(substring instring 1 (- (length instring) 2)))
(define (parse-fdo-file-line infile)
"Parse a `*.desktop' file list.
Returns (group1 (key1 . value1) ... group2 (keyA . valueA) ...)"
(when (setq this-line (read-line infile))
(if (not (fdo-skip-line-p this-line))
(cons
(if (fdo-group-p this-line)
(get-fdo-group this-line)
(get-key-value-pair this-line))
(parse-fdo-file-line infile))
(parse-fdo-file-line infile))))
(define (parse-fdo-file infile)
"Parse a `*.desktop' file and return an alist."
(format standard-error "Processing: %s\n" infile)
(when (desktop-file-p infile)
(let ((d-file (open-file infile 'read)))
(parse-fdo-file-line d-file))))
;; desktop-file mapping
(define (map-desk-files in-desk-files in-directory #!optional (extension "."))
"Given a list of filenames and a directory, will expand those
filenames to include the full path."
(when in-desk-files
(if (string-match extension (car in-desk-files))
(cons (expand-file-name (car in-desk-files) in-directory)
(map-desk-files (cdr in-desk-files) in-directory extension))
(map-desk-files (cdr in-desk-files) in-directory extension))))
(define (map-dir-files directories #!optional (extension "."))
"Given a list of directory paths, will return a list of
files in those direcories with their full pathnames. Optionally
`extension' may be set to show only files that match the regexp."
(when directories
(if (file-directory-p (car directories))
(let ((desk0 (directory-files (car directories))))
(cons (map-desk-files desk0 (car directories) extension)
(map-dir-files (cdr directories) extension)))
(map-dir-files (cdr directories) extension))))
(define (flatten input)
(cond ((null input) nil)
((atom input) (list input))
(t (append (flatten (car input))
(flatten (cdr input))))))
;; language functions
(defmacro simplify-mlang (mlang mlevel)
`(and
,(cond
((or (= 0 mlevel) (not mlevel))
`(or (string-looking-at "([a-z]*)(_?)([A-Z]*?)(@)([A-Z]*[a-z]*)?" ,mlang)
(string-looking-at "([a-z]*)(_..)|([a-z]*)?" ,mlang)
(string-looking-at "([a-z]*)?" ,mlang)))
((= 1 mlevel)
`(string-looking-at "([a-z]*)(_?)([A-Z]*?)(@)([A-Z]*[a-z]*)?" ,mlang))
((= 2 mlevel)
`(string-looking-at "([a-z]*)(_..)|([a-z]*)?" ,mlang))
((= 3 mlevel)
`(string-looking-at "([a-z]*)?" ,mlang)))
(expand-last-match "\&")))
(define (find-lang-string)
(let loop ((lang-vars '("LC_ALL" "LC_MESSAGES" "LANG")))
(and lang-vars
(let ((mlang (getenv (car lang-vars))))
(if mlang (simplify-mlang mlang 0)
(loop (cdr lang-vars)))))))
;; Functions for categories
(define (remove-duplicates input)
"Remove duplicate entries from `input'"
(do ((a '() (if (member (car input) a) a (cons (car input) a)))
(input input (cdr input)))
((null input) (reverse a))))
(define (merge-list input delimiter)
"Merge a cons list `input' into a string separated by `delimiter'"
(when input
(concat (car input) delimiter
(merge-list (cdr input) delimiter))))
(define (associate-categories fdol)
"Associate the `Categories' value(s) with the category
master list, `desktop-cat-alist'. Returns a modified desktop-file entry."
(when fdol
(let* ((these-categories
(delete "" (string-split ";" (cdr (assoc "Categories" fdol)))))
(category-list '()))
(let loop ((this-category these-categories))
(if (null this-category)
(let ((cat-string (merge-list (remove-duplicates category-list) ";")))
(rplacd (assoc "Categories" fdol)
cat-string)
fdol)
(progn (mapc (lambda (ent)
(if (member (car this-category) ent)
(setq category-list
(append category-list (list (car ent))))))
desktop-cat-alist)
(loop (cdr this-category))))))))
(define (grab-category input cat)
"Remove duplicate categories from a generated apps-menu list by
category name."
(when input
(let ((cat-list '()))
(setq cat-list (append cat-list (list cat)))
(let loop ((this-line input))
(if (not this-line) cat-list
(progn (if (string= (caar this-line) cat)
(setq cat-list (append cat-list (list (cdr (car this-line))))))
(loop (cdr this-line))))))))
(define (make-category-list input)
"Return a list of the categories to be used in the menu."
(when input
(cons (caar input)
(make-category-list (cdr input)))))
(define (consolidate-menu input)
"Reduce the menu down so that each menu entry is inside a
single category."
(when input
(let ((cat-list (remove-duplicates (make-category-list input)))
(out-menu nil))
(mapc (lambda (x)
(setq out-menu
(append out-menu
(list (remove-duplicates (grab-category input x))))))
cat-list)
out-menu)))
;; In fact, %% means "escaped %". Let's forget :/
(define (trim-percent string)
"Cut the string before % sign if present."
(if (string-match "%" string)
(substring string 0 (match-start))
string))
(define (alphabetize-entries saw-menu)
"Alphabetize the entries in the category menus."
(if saw-menu
(cons (cons (car (car saw-menu))
(sort (cdr (car saw-menu))
(lambda (a b)
(string< (string-downcase (car a)) (string-downcase (car b))))))
(alphabetize-entries (cdr saw-menu)))))
(define (fdo-exile fdo-list)
"Exile `fdo-list' -- i.e., mark it as an invalid or garbled
desktop file."
(let ((exile-comment
(cons "fdo-Comment" "This .desktop file was exiled, use \
with caution, file may be corrupt.\n"))
(exile-cmd
(cons "Exec" "sawfish-client -c 'display-errors'\n")))
;; Set the fdo-Comment key, mentioning the exile.
(setq fdo-list (append fdo-list (list exile-comment)))
;; Set the NoDisplay key to 'true'
(if (assoc "NoDisplay" fdo-list)
(rplacd (assoc "NoDisplay" fdo-list) "true")
(setq fdo-list (append fdo-list (cons (cons "NoDisplay" "true")))))
;; Set the Categories & Category keys to 'Exile'
(if (assoc "Categories" fdo-list)
(rplacd (assoc "Categories" fdo-list) "Exile")
(setq fdo-list (append fdo-list (cons (cons "Categories" "Exile")))))
(if (assoc "Category" fdo-list)
(rplacd (assoc "Category" fdo-list) "Exile")
(setq fdo-list (append fdo-list (cons (cons "Category" "Exile")))))
;; Set the Exec key if it does not exist
(when (not (assoc "Exec" fdo-list))
(setq fdo-list (append fdo-list (list exile-cmd))))
;; Set the Name key if it does not exist
(when (and (not (assoc "Name" fdo-list))
(not (assoc (concat name-string apps-menu-lang "]") fdo-list)))
(setq fdo-list (append fdo-list (cons (cons "Name" "Unknown")))))
fdo-list))
(define (fdo-check-exile fdo-list)
"If `fdo-list' doesn't have a Categories, Exec, or Name field,
exile it."
(when fdo-list
(if (or (and (not (assoc "Categories" fdo-list))
(not (stringp (cdr (assoc "Categories" fdo-list))))
(not (assoc "Category" fdo-list))
(not (stringp (cdr (assoc "Categories" fdo-list)))))
(not (assoc "Exec" fdo-list))
(and (not (assoc "Name" fdo-list))
(not (assoc (concat name-string
apps-menu-lang "]")
fdo-list))))
(fdo-exile fdo-list)
fdo-list)))
(define (fdo-double-check-category fdo-list)
"Make sure the Category key is present and correctly asigned."
(when fdo-list
(if (assoc "Category" fdo-list)
(if (or (not (stringp (cdr (assoc "Category" fdo-list))))
(equal "" (cdr (assoc "Category" fdo-list)))
(not (stringp (cdr (assoc "Category" fdo-list)))))
(rplacd (assoc "Category" fdo-list) "Exile"))
(append fdo-list (cons (cons "Category" "Exile")))))
fdo-list)
(define (determine-desktop-name fdo-list)
"Get the correct Name[*] entry based on language settings."
(or (when apps-menu-lang
(let ((mlang-1 (concat name-string (simplify-mlang apps-menu-lang 1) "]"))
(mlang-2 (concat name-string (simplify-mlang apps-menu-lang 2) "]"))
(mlang-3 (concat name-string (simplify-mlang apps-menu-lang 3) "]")))
(or (cdr (assoc mlang-1 fdo-list))
(cdr (assoc mlang-2 fdo-list))
(cdr (assoc mlang-3 fdo-list)))))
(cdr (assoc "Name" fdo-list))))
(define (determine-desktop-exec fdo-list)
"Determine the correct `(system exec)' function from the given fdo alist"
(if (assoc "Terminal" fdo-list)
(if (string-match "[Tt]" (cdr (assoc "Terminal" fdo-list)))
(list 'system
(concat xterm-program " -e "
(trim-percent (cdr (assoc "Exec" fdo-list)))
" &"))
(list 'system
(concat (trim-percent (cdr (assoc "Exec" fdo-list)))
" &")))
(list 'system
(concat (trim-percent (cdr (assoc "Exec" fdo-list)))
" &"))))
;; Apps-Menu Filtering
(define (fdo-nodisplay-filter fdol)
"Return the desktop-file-list if NoDisplay is False, or if NoDisplay is
not present in the desktop-file-list"
(if (assoc "NoDisplay" fdol)
(if (string-match "[Ff]" (cdr (assoc "NoDisplay" fdol)))
fdol)
fdol))
(define (fdo-hidden-filter fdol)
"Return the desktop-file-list if Hidden is False, or if Hidden is
not present in the desktop-file-list"
(if (assoc "Hidden" fdol)
(if (string-match "[Ff]" (string-downcase (cdr (assoc "OnlyShowIn" fdol))))
fdol)
fdol))
(define (fdo-onlyshowin-filter fdol)
"Return the desktop-file-list if OnlyShowIn matches `desktop-environment',
or if OnlyShowIn is not present in the desktop-file-list"
(if (assoc "OnlyShowIn" fdol)
(if (string-match desktop-environment (string-downcase (cdr (assoc "OnlyShowIn" fdol))))
fdol)
fdol))
(define (fdo-notshowin-filter fdol)
"Return the desktop-file-list if NotShowIn does not match `desktop-environment',
or if NotShowIn is not present in the desktop-file-list"
(if (assoc "NotShowIn" fdol)
(if (not (string-match desktop-environment (string-downcase (cdr (assoc "NotShowIn" fdol)))))
fdol)
fdol))
(define (fdo-associate-categories-filter fdol)
"If `apps-menu-associate-categories' is true, filter the
desktop-entry through `fdo-associate-categories'."
(when fdol
(if apps-menu-associate-categories
(associate-categories fdol)
fdol)))
(define (fdo-toplevel-filter fdol)
"Return the desktop-file-list if the `Category' is of the
Top-Level variety."
(when fdol
(if (not (equal "Top-Level" (cdr (assoc "Category" fdol))))
fdol)))
(define (fdo-default-filter fdol)
"The default fdo-filter, combines the above."
(fdo-toplevel-filter
(fdo-hidden-filter
(fdo-notshowin-filter
(fdo-onlyshowin-filter
(fdo-nodisplay-filter fdol))))))
(define (fdo-some-filter fdol)
"The 'some fdo-filter, will only respect
the NotShowIn and OnlyShowIn keys."
(fdo-toplevel-filter
(fdo-notshowin-filter
(fdo-onlyshowin-filter fdol))))
(define (fdo-filter-record fdol filter)
"Let `filter' process `fdol', a desktop file entry, and return the result.
`filter' can be a function, or a symbol 'default or 'some. If it isn't set,
return `fdol' as-is."
(if (not filter) fdol
(condition-case nil
(let loop ((fdo-entry fdol))
(when (consp fdo-entry)
(cons
;; Check if entry is valid
(fdo-double-check-category
(fdo-check-exile
((cond
;; default filter is chosen
((equal filter 'default)
fdo-default-filter)
;; some flter is chosen
((equal filter 'some)
fdo-some-filter)
;; user filter is chosen
((closurep filter)
filter))
(car fdo-entry))))
(loop (cdr fdo-entry)))))
(error fdol))))
(define (split-desktop-entry fdol)
"Split a desktop entry into several entries, each containing one
of the categories of the original."
(when fdol
(let ((new-fdol fdol)
(category-string (cdr (assoc "Categories" fdol))))
(when (stringp category-string)
(let loop ((categories
(delete "" (string-split ";" category-string))))
(when categories
(append (list
(append new-fdol (list (cons "Category" (car categories)))))
(loop (cdr categories)))))))))
;; Sawfish-menu generation
(define (fdo-menu-entry fdol)
"Return menu-entry list from a fdo-list."
;; Generate the menu-entry list
(generate-menu-entry
;; Filter entry by pre-made or user function
(delete nil
(fdo-filter-record
;; Split the desktop-entry by category
(split-desktop-entry
;; Rename 'Categories' key based on category-list
(fdo-associate-categories-filter
;; Check if entry is valid
(fdo-check-exile fdol)))
apps-menu-filter))))
(define (generate-menu-entry fdo-list)
"Generate a menu entry to run the program specified in the the
desktop file `desk-file'."
(when (car fdo-list)
(cons (list (cdr (assoc "Category" (car fdo-list)))
(determine-desktop-name (car fdo-list))
(determine-desktop-exec (car fdo-list)))
(generate-menu-entry (cdr fdo-list)))))
(define (generate-apps-menu)
"Returns the list of applications menu which can be used for `apps-menu'."
(unless apps-menu-lang
(setq apps-menu-lang (find-lang-string)))
(let ((desk-files (flatten (map-dir-files desktop-directory ".desktop")))
(local-menu nil))
(mapc
(lambda (x)
(setq local-menu
(append local-menu
(fdo-menu-entry
(parse-fdo-file x)))))
desk-files)
(if apps-menu-alphabetize
(alphabetize-entries (consolidate-menu (sort (delete nil local-menu) string<)))
(consolidate-menu (sort (delete nil local-menu) string<)))))
(define (init-apps-menu)
"If `apps-menu' is nil, then call `update-apps-menu'. This function
is intended to be called during Sawfish initialization."
(unless apps-menu
(update-apps-menu)))
(define (update-apps-menu)
"Set `apps-menu' to `user-apps-menu', and if `apps-menu-autogen' is non-nil,
append the auto generated one."
(if apps-menu-autogen
(setq apps-menu
(append user-apps-menu (generate-apps-menu)))
(setq apps-menu user-apps-menu)))
(define-command 'update-apps-menu update-apps-menu)
)