diff options
-rw-r--r-- | .gitignore | 68 | ||||
-rw-r--r-- | actions/Makefile.am | 4 | ||||
-rw-r--r-- | compile_commands.json | 56 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rw-r--r-- | docs/README.md | 14 | ||||
-rw-r--r-- | docs/terminal-media-launcher-config.md | 284 | ||||
-rw-r--r-- | docs/windows_compile_instructions.md | 2 | ||||
-rw-r--r-- | man/terminal-media-launcher-config.5 | 323 | ||||
-rw-r--r-- | man/terminal-media-launcher.1 | 6 | ||||
-rw-r--r-- | spec/terminal-media-launcher.spec.in | 2 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/draw.c | 55 | ||||
-rw-r--r-- | src/entry.c | 84 | ||||
-rw-r--r-- | src/group.c | 190 | ||||
-rw-r--r-- | src/include/cache.h | 2 | ||||
-rw-r--r-- | src/include/draw.h | 5 | ||||
-rw-r--r-- | src/include/entry.h | 18 | ||||
-rw-r--r-- | src/include/group.h | 29 | ||||
-rw-r--r-- | src/include/read_cfg.h | 15 | ||||
-rw-r--r-- | src/read_cfg.c | 738 | ||||
-rw-r--r-- | src/unix/read_cfg.c | 210 | ||||
-rw-r--r-- | src/windows/draw.c | 4 | ||||
-rw-r--r-- | src/windows/read_cfg.c | 204 |
24 files changed, 1021 insertions, 1302 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cc2a44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.cache +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + +# Additional ignores +.cache +*~ +*.tar.gz +config.h +actions/build +nsis/installer.nsi +spec/terminal-media-launcher.spec +src/icon/*.res +src/terminal-media-launcher +src/*.dll +src/*.exe +src/*.o +src/unix/*.o +src/windows/*.o diff --git a/actions/Makefile.am b/actions/Makefile.am index a48c772..e3451aa 100644 --- a/actions/Makefile.am +++ b/actions/Makefile.am @@ -8,7 +8,7 @@ clean-local: actions_build_deb: IMAGE_NAME = debian actions_build_deb: IMAGE_VERSION = unstable actions_build_deb: CONTAINER_NAME = $(PACKAGE_NAME)_$@_$(IMAGE_NAME)-$(IMAGE_VERSION) -actions_build_deb: DEPENDENCY_LIST = autoconf-archive debmake devscripts ncurses-dev pkg-config +actions_build_deb: DEPENDENCY_LIST = autoconf-archive debmake devscripts liblua5.1-0-dev lua5.1 ncurses-dev pkg-config actions_build_deb: ../$(PACKAGE_NAME)-$(PACKAGE_VERSION).tar.gz podman pull docker.io/library/$(IMAGE_NAME):$(IMAGE_VERSION) podman container exists "$(CONTAINER_NAME)" || podman run -itd --name "$(CONTAINER_NAME)" "$(IMAGE_NAME):$(IMAGE_VERSION)" @@ -26,7 +26,7 @@ actions_build_deb: ../$(PACKAGE_NAME)-$(PACKAGE_VERSION).tar.gz actions_build_rpm: IMAGE_NAME = fedora actions_build_rpm: IMAGE_VERSION = rawhide actions_build_rpm: CONTAINER_NAME = $(PACKAGE_NAME)_$@_$(IMAGE_NAME)-$(IMAGE_VERSION) -actions_build_rpm: DEPENDENCY_LIST = gcc make ncurses-devel rpmdevtools rpmlint +actions_build_rpm: DEPENDENCY_LIST = gcc lua lua-devel make ncurses-devel rpmdevtools rpmlint actions_build_rpm: ../$(PACKAGE_NAME)-$(PACKAGE_VERSION).tar.gz podman pull docker.io/library/$(IMAGE_NAME):$(IMAGE_VERSION) podman container exists "$(CONTAINER_NAME)" || podman run -itd --name "$(CONTAINER_NAME)" "$(IMAGE_NAME):$(IMAGE_VERSION)" diff --git a/compile_commands.json b/compile_commands.json index cfbbf2d..cdb203b 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -7,6 +7,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -14,9 +15,9 @@ "terminal_media_launcher-cache.o", "cache.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/cache.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/terminal_media_launcher-cache.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/cache.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/terminal_media_launcher-cache.o" }, { "arguments": [ @@ -26,6 +27,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -33,9 +35,9 @@ "terminal_media_launcher-draw.o", "draw.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/draw.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/terminal_media_launcher-draw.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/draw.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/terminal_media_launcher-draw.o" }, { "arguments": [ @@ -45,6 +47,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -52,9 +55,9 @@ "terminal_media_launcher-read_cfg.o", "read_cfg.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/read_cfg.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/terminal_media_launcher-read_cfg.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/read_cfg.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/terminal_media_launcher-read_cfg.o" }, { "arguments": [ @@ -64,6 +67,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -71,9 +75,9 @@ "terminal_media_launcher-group.o", "group.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/group.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/terminal_media_launcher-group.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/group.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/terminal_media_launcher-group.o" }, { "arguments": [ @@ -83,6 +87,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -90,9 +95,9 @@ "terminal_media_launcher-entry.o", "entry.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/entry.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/terminal_media_launcher-entry.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/entry.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/terminal_media_launcher-entry.o" }, { "arguments": [ @@ -102,6 +107,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -109,9 +115,9 @@ "unix/terminal_media_launcher-cache.o", "unix/cache.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/cache.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/terminal_media_launcher-cache.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/unix/cache.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/unix/terminal_media_launcher-cache.o" }, { "arguments": [ @@ -121,6 +127,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -128,9 +135,9 @@ "unix/terminal_media_launcher-draw.o", "unix/draw.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/draw.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/terminal_media_launcher-draw.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/unix/draw.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/unix/terminal_media_launcher-draw.o" }, { "arguments": [ @@ -140,6 +147,7 @@ "-I..", "-D_DEFAULT_SOURCE", "-D_XOPEN_SOURCE=600", + "-I/usr/include/lua5.1", "-g", "-O2", "-c", @@ -147,8 +155,8 @@ "unix/terminal_media_launcher-read_cfg.o", "unix/read_cfg.c" ], - "directory": "/home/louie/Documents/code/terminal-media-launcher/source/src", - "file": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/read_cfg.c", - "output": "/home/louie/Documents/code/terminal-media-launcher/source/src/unix/terminal_media_launcher-read_cfg.o" + "directory": "/home/louie/Documents/code/terminal-media-launcher/src", + "file": "/home/louie/Documents/code/terminal-media-launcher/src/unix/read_cfg.c", + "output": "/home/louie/Documents/code/terminal-media-launcher/src/unix/terminal_media_launcher-read_cfg.o" } ] diff --git a/configure.ac b/configure.ac index 2803974..72d7df2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([terminal-media-launcher],[0.1.1a]) +AC_INIT([terminal-media-launcher],[0.2]) AC_SUBST([PACKAGE_TITLE], ["Terminal Media Launcher"]) AC_CONFIG_SRCDIR([src/draw.c]) AC_CONFIG_HEADERS([config.h]) @@ -7,12 +7,15 @@ AM_INIT_AUTOMAKE([foreign]) # Checks for programs. AC_PROG_CC +AX_PROG_LUA(,,,[AC_MSG_ERROR([requires lua])]) # Checks for libraries. AC_CHECK_LIB([ncurses], [initscr]) +AX_LUA_LIBS # Checks for header files. AC_CHECK_HEADERS([stdlib.h string.h unistd.h]) +AX_LUA_HEADERS # Checks for typedefs, structures, and compiler characteristics. AC_CHECK_HEADER_STDBOOL diff --git a/debian/control b/debian/control index 86a867c..fc7c251 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,7 @@ Package: terminal-media-launcher Architecture: amd64 Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} +Recommends: lua-filesystem Description: Lightweight Terminal Media Launcher written in C Terminal Media Launcher is a command line utility to help streamline launching applications and other media diff --git a/docs/README.md b/docs/README.md index cb17fd0..ed1f06a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,20 +6,18 @@ ## Compiling and Running -For Windows instructions, see [here](windows_compile_instructions.md) - -terminal-media-launcher can be compiled on any system with make, gcc, and the ncurses development library installed. It can be compiled and installed on any Linux distribution, and can also be compiled and run on Microsoft Windows. To compile and run terminal-media-launcher: +terminal-media-launcher can be compiled on any system with make, gcc, and the ncurses development library installed. It can be compiled and installed on any Linux distribution, and can also be compiled and run on Microsoft Windows using MinGW (e.g., via [MSYS2](https://www.msys2.org/)). To compile and run terminal-media-launcher: 1. Install dependencies. On Debian and Debian-based systems: ``` -# apt install gcc libncurses-dev make +# apt install gcc liblua5.1-0-dev lua5.1 make ncurses-dev pkg-config ``` On RHEL and RHEL-based systems: ``` -# dnf install gcc ncurses-devel +# dnf install gcc lua lua-devel make ncurses-devel ``` 2. Download the latest `.tar.gz` release @@ -76,12 +74,12 @@ By default, terminal-media-launcher searches in the following order for a config ### Linux -1. `$HOME/.config/terminal-media-launcher/config` -2. `$HOME/.terminal-media-launcher/config` +1. `$HOME/.config/terminal-media-launcher/config.lua` +2. `$HOME/.terminal-media-launcher/config.lua` ### Windows -1. `%APPDATA%\terminal-media-launcher\config` +1. `%APPDATA%\terminal-media-launcher\config.lua` A different configuration file location can also be specified with the `-c` flag: diff --git a/docs/terminal-media-launcher-config.md b/docs/terminal-media-launcher-config.md index 3d12f97..a5bf674 100644 --- a/docs/terminal-media-launcher-config.md +++ b/docs/terminal-media-launcher-config.md @@ -1,106 +1,146 @@ ## Introduction -**config** specifies settings for Terminal Media Launcher, including preferences, groups, entries, and file locations. Each line of `config` is read by terminal-media-launcher unless the line is empty or the line begins with a '#'. terminal-media-launcher can automatically generate a configuration file if no such file is found. An automatically generated configuration file will create groups for Music, Pictures, and Videos, and add entries to each group from the respective directory in the user's home directory. It is highly recommended that the user edit the configuration file manually. +Starting in version 0.2, Terminal Media Launcher is configured via a [lua](https://www.lua.org/) script. This project assumes the configuration will adhere to lua 5.1, but is written to be as agnostic as possbile. The general format of the configuration is outlined below: + +```lua +Groups = { + { + name = <string>, + Launcher = <string>, + Flags = <string>, + Entries = { + { + name = <string>, + path = <string> + }, + ... + } + }, + ... +} +``` + +<!-- +**`config.lua`** specifies settings for Terminal Media Launcher, including preferences, groups, entries, and file locations. Each line of `config` is read by terminal-media-launcher unless the line is empty or the line begins with a '#'. terminal-media-launcher can automatically generate a configuration file if no such file is found. An automatically generated configuration file will create groups for Music, Pictures, and Videos, and add entries to each group from the respective directory in the user's home directory. It is highly recommended that the user edit the configuration file manually. +--> ## Table of Contents - [Creating a Group](#CreatingAGroup) - - [addGroup](#addgroup) - - [setLauncher](#setlauncher) - - [setLauncherRaw](#setlauncherraw) - - [setFlags](#setflags) - [Adding Entries](#AddingEntries) - - [add](#add) - - [addF](#addf) - - [addName](#addname) - - [addNameF](#addnamef) - - [addR](#addr) - - [hide](#hide) - - [hideFile](#hidefile) + - [Adding Entries using lfs module](#AddingEntriesLfs) - [Settings](#settings) - - [autoAlias](#autoalias) - - [foldCase](#foldcase) - [sort](#sort) - [Example](#example) ## Creating a Group <a name="CreatingAGroup"></a> -terminal-media-launcher will not work without any groups, so you will need to know how to create a group. - -### addGroup - -- **addGroup** *name* - -`addGroup` will create a new group with a specified name. By default this group is empty, with zero entries, no launching application specified, and no flags specified. If there is a space in the name, it must be written in quotes (ex. "TV Shows") - -### setLauncher - -- **setLauncher** *group* */path/to/launcher* - -`setLauncher` will set a group's launching application. If no launching application is specified for a group, terminal-media-launcher will treat each entry in that group as an executable file. If there is a space in the path to the launching application, it must be written in quotes (ex. "/usr/bin/my launcher"). *Keep in mind that the path to the launching application should be absolute*. - -### setLauncherRaw - -- **setLauncherRaw** *group* */path/to/launcher* - -`setLauncherRaw` is identical to `setLauncher` with the exception that the launcher application specified will not be wrapped in quotes for the system call when a member of the group is launched. This can be used to specify more complex launching instructions. - -### setFlags - -- **setFlags** *group* *flags* - -`setFlags` will set the flags to be specified for the launching application. If no launching application is specified, any specified flags are ignored. If the specified flags contain a space, they must be written in quotes. +The config must contain at least one valid group. Groups are to be inserted in the `Groups` global variable. `Groups` is an array of tables. Each group is a table containing the following keys: + +- Mandatory keys: + - `name` *string* - a name for the group + - `Entries` *table* - an array of tables. Each table represents an entry. +- Optional keys: + - `Launcher` *string* - the path for a program that launches the entries of this group. If not set, it assumes the entries in this group are executables (and have no launching program) + - `Flags` *string* - flags to pass to the launching program. If not set, no flags are passed + +Here is a helper function you can use in your config to add a group + +```lua +local function addGroup(name, launcher, flags) + assert(type(name) == "string") + + -- create Groups table if needed + if Groups == nil then + Groups = {} + end + + local new_group = {} + new_group.name = name + new_group.Entries = {} + if launcher ~= nil then + new_group.Launcher = launcher + end + if flags ~= nil then + new_group.Flags = flags + end + + table.insert(Groups, new_group) + return new_group +end +``` ## Adding Entries <a name=AddingEntries></a> -terminal-media-launcher will hide empty groups, so you will need to know how to add entries to a group. - -### add - -- **add** *path/to/file(s)* *group* - -`add` will add a file to a specified group if the path exists. It can also be used to add mutiple files to a group in one line using the '\*' operator (ex. `add /home/john/Pictures/* Pictures`). If the path to the file(s) contains space(s), it must be written in quotes. - -### addF - -- **addF** *new-entry* *group* - -`addF` will force an entry to be added to a specified group, regardless as to whether it is a valid file or not. Unlike `add`, `addF`'s argument does not need to be a valid file, but `addF` can only specify a single entry and does not support the '\*' operator. If the arg has a space in it, it must be written in quotes. +For a group to be valid, it must contain at least one valid entry. Entries are to be inserted in the `Entries` key of a group table. `Entries` is an array of tables. Each entry is a table containing the following keys: -### addName +- Mandatory keys: + - `name` *string* - a name for the entry +- Optional keys: + - `path` *string* - a path to the file associated with this entry. If not set, the path is the same as the name -- **addName** *name* *path/to/file* *group* +Here is a helper function you can use in your config to add an entry to a group -`addName`, like `add`, will add an entry to a specified group if the path exists. `addName` allows for a name to be specified for this entry (by default, the name is the same as the file name). Unlike `add`, only one entry can be added per line, as `addName` does not support the '\*' operator. If either the name or file path contain a space, they must be written in quotes. +```lua +local function addEntry(parentGroup, name, path) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(name) == "string") -### addNameF + local new_entry = {} + new_entry.name = name + if path ~= nil then + new_entry.path = path + end -- **addNameF** *name* *new-entry* *group* - -`addNameF` can be used in place of `addF` if you want the forced argument to have a different name displayed for the entry than is called in the system call to launch the entry. Otherwise, it is effectively the same as `addF` - -### addR - -- **addR** *path/to/files* *group* - -`addR` will recursively add entries to a group. `addR` functions like `add`, but will also search sub-directories for matches. - -### hide - -- **hide** *entry* *group* - -`hide` will remove a specified entry from a specified group. The entry argument should refer to the entry's name, rather than the entry's path. This option may be useful to hide certain entries after adding entries with the '\*' operator. *At the moment, hide can only hide a single entry*. - -### hideFile - -- **hideFile** *path* *group* + table.insert(parentGroup.Entries, new_entry) + return new_entry +end +``` -`hideFile` has the exact same functionality as `hide`, but takes the absolute path of the entry to hide as the first argument, instead of the name. +### Adding Entries using lfs module <a name=AddingEntriesLfs></a> + +It is recommended to install the [lua-filesystem](https://github.com/lunarmodules/luafilesystem) module and use it to add entries more efficiently. This example can be used to descend into a directory and add files matching a `string.match` pattern to a group. + +```lua +local lfs = require "lfs" + +local function addEntries(parentGroup, startDir, filePattern, recursive) + -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(startDir) == "string") + assert(type(filePattern) == "string") + + for file in lfs.dir(startDir) do + local fullFilePath = startDir .. "/" .. file + if file ~= "." and file ~= ".." then + -- descend into subdirectory if recursive is set to true + if lfs.attributes(fullFilePath).mode == "directory" and recursive == true then + addEntries(parentGroup, fullFilePath, filePattern, recursive) + elseif lfs.attributes(fullFilePath).mode == "file" then + if string.match(file, filePattern) then + table.insert(parentGroup.Entries, { + name = file, + path = fullFilePath + }) + end + end + end + end +end +``` ## Settings -If any of the following settings are specified, they should be at the top of the config file. +Settings can be set via the `Settings` global variable. +```lua +Settings = {} +Settings.sort = <boolean> +``` + +<!-- ### autoAlias - **autoAlias** *on/off* @@ -119,38 +159,72 @@ If any of the following settings are specified, they should be at the top of the - **foldCase** *on/off* Entering any non-traversal input in terminal-media-launcher can be used to jump to a group or entry. For instance, hitting 'f' on the keyboard will jump the cursor to the next group or entry that starts with an 'f'. *foldCase* determines whether or not this functionality is **case insensitive (on)** or **case sensitive (off)**. *foldCase* is turned on by default. +--> ### sort -- **sort** *on/off* - -`sort` will sort entries of each group in alphabetical order (though not the list of groups). Turning off `sort` is only recommended when adding one item per line to a group. `sort` is turned on by default. +`sort` will sort entries of each group in alphabetical order (though not the list of groups). `sort` is true by default. ## Example -``` -autoAlias on - -# Adding a Group of Various Applications - -addGroup Applications -addName GIMP /usr/bin/gimp Applications -addName Chromium /usr/bin/chromium-browser Applications -addName Thunderbird /usr/bin/thunderbird Applications - -# Adding a Videos Group that contains mp4 files - -addGroup Videos -setLauncher Videos /usr/bin/vlc -add /home/john/Videos/*mp4 Videos - -# Adding a Pictures Group that contains only jpg and png files as well as all files from an external drive and a single desktop wallpaper - -addGroup Pictures -setLauncher Pictures /usr/bin/sxiv -setFlags Pictures "-s f" -add /home/john/Pictures/*jpg Pictures -add /home/john/Pictures/*png Pictures -addR "/mnt/External_Drive/Johns Photos/*" Pictures -addName "My Desktop Background" "/mnt/External_Drive/desktop wallpaper.jpg" Pictures +Here is an example `config.lua` which creates a Music, Pictures, and, Videos group, and adds all the files from the user's Music, Pictures, and Videos home folders to each group. + +```lua +local lfs = require "lfs" + +local function addGroup(name, launcher, flags) + assert(type(name) == "string") + + -- create Groups table if needed + if Groups == nil then + Groups = {} + end + + local new_group = {} + new_group.name = name + new_group.Entries = {} + if launcher ~= nil then + new_group.Launcher = launcher + end + if flags ~= nil then + new_group.Flags = flags + end + + table.insert(Groups, new_group) + return new_group +end + +local function addEntries(parentGroup, startDir, filePattern, recursive) + -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(startDir) == "string") + assert(type(filePattern) == "string") + + for file in lfs.dir(startDir) do + local fullFilePath = startDir .. "/" .. file + if file ~= "." and file ~= ".." then + -- descend into subdirectory if recursive is set to true + if lfs.attributes(fullFilePath).mode == "directory" and recursive == true then + addEntries(parentGroup, fullFilePath, filePattern, recursive) + elseif lfs.attributes(fullFilePath).mode == "file" then + if string.match(file, filePattern) then + table.insert(parentGroup.Entries, { + name = file, + path = fullFilePath + }) + end + end + end + end +end + +local music = addGroup("Music", "xdg-open") +addEntries(music, os.getenv("HOME") .. "/Music", ".*", true) + +local pictures = addGroup("Pictures", "xdg-open") +addEntries(pictures, os.getenv("HOME") .. "/Pictures", ".*", true) + +local videos = addGroup("Videos", "xdg-open") +addEntries(videos, os.getenv("HOME") .. "/Videos", ".*", true) ``` diff --git a/docs/windows_compile_instructions.md b/docs/windows_compile_instructions.md index 757dece..4174693 100644 --- a/docs/windows_compile_instructions.md +++ b/docs/windows_compile_instructions.md @@ -1,5 +1,7 @@ # Windows Compilation Instructions +**NOTE: Outdated. Windows users should be just fine following along with instructions on the main README.** + Compiling on Windows requires access to the MinGW toolchain. The easiest way to get the necessary libraries is by installing [MSYS2](https://www.msys2.org/) which provides a package manager: pacman. 1. Download the latest version of MSYS2 from [here](https://www.msys2.org/). diff --git a/man/terminal-media-launcher-config.5 b/man/terminal-media-launcher-config.5 index 60b814b..0a818b9 100644 --- a/man/terminal-media-launcher-config.5 +++ b/man/terminal-media-launcher-config.5 @@ -1,132 +1,219 @@ -.TH TERMINAL-MEDIA-LAUNCHER-CONFIG 5 +.\" generated with Ronn-NG/v0.9.1 +.TH "TERMINAL\-MEDIA\-LAUNCHER\-CONFIG" 5 -.SH NAME +.SH "NAME" terminal-media-launcher-config - terminal-media-launcher configuration file .SH SYNOPSIS -~/.config/terminal-media-launcher/config (or ~/.terminal-media-launcher/config) +~/.config/terminal-media-launcher/config.lua (or ~/.terminal-media-launcher/config.lua) .SH DESCRIPTION -The configuration file for terminal-media-launcher is a plain text file where commands regarding media groups and entries, along with various settings regarding layout, are specified. These commands are described in the next section. Each line is evaluated on its own, while empty lines and lines beginning with # are ignored. - -A configuration file can be automatically generated if no such file is found. An automatically generated configuration file will create groups for Music, Pictures, and Videos, and add entries to each group from the respective directory in the user's home directory. It is highly recommended that the user edit the configuration file manually. +Starting in version 0\.2, Terminal Media Launcher is configured via a lua \fIhttps://www\.lua\.org/\fR script\. This project assumes the configuration will adhere to lua 5\.1, but is written to be as agnostic as possbile\. The general format of the configuration is outlined below: +.P +.nf +.BI "Groups = { " +.BI " { " +.BI " name = <string>, " +.BI " Launcher = <string>, " +.BI " Flags = <string>, +.BI " Entries = { " +.BI " { " +.BI " name = <string>, " +.BI " path = <string> " +.BI " }, " +.BI " ... " +.BI " } " +.BI " }, " +.BI " ... " +.BI "}" .SH COMMANDS -.SS Creating a Group -\fIterminal-media-launcher-config\fR requires at least one group. - -\fBaddGroup\fR <name> -.RS -This command will create a new group with a specified name. By default, a new group is empty, with zero entries, no launching application specified, and no flags specified. If their is a space in the name, it must be written in quotes (ex. "TV Shows"). -.RE - -\fBsetLauncher\fR <group> <path> -.RS -This command will set a group's launching application. If no launching application is specified for a group, terminal-media-launcher will treat each entry in that group as an executable file. If their is a space in the path to the launching application, it must be written in quotes (ex. "/usr/bin/my launcher"). The path to the launching application should be absolute. -.RE - -\fBsetLauncherRaw\fR <group> <path> -.RS -This command is identical to \fIsetLauncher\fR with the exception that the launcher application specified will not be wrapped in quotes for the system call when a member of the group is launched. This can be used to specify more complex launching instructions. -.RE - -\fBsetFlags\fR <group> <flags> -.RS -This command will set the flags to be specified for the launching application. If no launching application is specified, any specified flags are ignored. If the specified flags contain a space, they must be written in quotes. -.RE - -.SS Adding Entries -In order for a created group to be valid, it must contain at least one entry. - -\fBadd\fR <path> <group> -.RS -This command will add a file to a specified group if the path exists. It can also be used to add mutiple files to a group in one line using the '*' operator (ex. add /home/john/Pictures/* Pictures). If the path to the file(s) contains space(s), it must be written in quotes. -.RE - -\fBaddF\fR <new entry> <group> -.RS -This command will force an entry to be added to a specified group, regardless as to whether it is a valid file or not. Unlike \fIadd\fR, \fIaddF\fR's argument does not need to be a valid file, but \fIaddF\fR can only specify a single entry and does not support the '*' operator. If the arg has a space in it, it must be written in quotes. -.RE - -\fBaddName\fR <name> <path> <group> -.RS -This command, like \fIadd\fR, will add an entry to a specified group if the path exists. \fIaddName\fR allows for a name to be specified for this entry (by default, the name is the same as the file name). Unlike \fIadd\fR, only one entry can be added per line, as \fIaddName\fR does not support the '*' operator. If either the name or file path contain a space, they must be written in quotes. -.RE - -\fBaddNameF\fR <name> <new-entry> <group> -.RS -This command can be used in place of \fIaddF\fR if you want the forced argument to have a different name displayed for the entry than is called in the system call to launch the entry. Otherwise, it is effectively the same as \fIaddF\fR. -.RE - -\fBaddR\fR <path> <group> -.RS -This command will recursively add entries to a group. \fIaddR\fR functions like \fIadd\fR, but will also search sub-directories for matches. -.RE - -\fBhide\fR <entry> <group> -.RS -This command will remove a specified entry from a specified group. The entry argument should refer to the entry's name, rather than the entry's path. This option may be useful to hide certain entries after adding many entries with the '*' operator. -.RE - -\fBhideFile\fR <path> <group> -.RS -This command has the exact same functionality as \fIhide\fR, but takes the absolute path of the entry to hide as the first argument, instead of the name. -.RE - -.SS Settings -Settings should be specified at the top of \fIterminal-media-launcher-config\fR - -\fBautoAlias\fR on|off -.RS -This setting will attempt to automatically give entries more human-readable names by: - - 1. Removing any characters inside parenthesis (including parenthesis) - 2. Replacing '-' and '\_' with a space character - 3. Replacing cases of multiple spaces in a row with only one space - 4. Removing file extensions (if the file has an extension) - -\fIautoAlias\fR is turned off by default. -.RE - -\fBfoldCase\fR on|off -.RS -Entering any non-traversal input in terminal-media-launcher can be used to jump to a group or entry. For instance, hitting 'f' on the keyboard will jump the cursor to the next group or entry that starts with an 'f'. This setting determines whether or not this functionality is case insensitive (on) or case sensitive (off). \fIfoldCase\fR is turned on by default. -.RE - -\fBsort\fR on|off -.RS -This setting will sort entries of each group in alphabetical order (though not the list of groups). Turning off \fIsort\fR is only recommended when adding one item per line to a group. \fIsort\fR is turned on by default. -.RE - -.SH EXAMPLE - +.SS "Creating a Group" +The config must contain at least one valid group\. Groups are to be inserted in the \fBGroups\fR global variable\. \fBGroups\fR is an array of tables\. Each group is a table containing the following keys: + +.IP "Mandatory keys:" +.IP "\[ci]" 4 +\fBname\fR \fIstring\fR \- a name for the group +.IP "\[ci]" 4 +\fBEntries\fR \fItable\fR \- an array of tables\. Each table represents an entry\. +.IP "" 0 + +.IP "Optional keys:" +.IP "\[ci]" 4 +\fBLauncher\fR \fIstring\fR \- the path for a program that launches the entries of this group\. If not set, it assumes the entries in this group are executables (and have no launching program) +.IP "\[ci]" 4 +\fBFlags\fR \fIstring\fR \- flags to pass to the launching program\. If not set, no flags are passed +.IP "" 0 + +.IP "" 0 +.P +Here is a helper function you can use in your config to add a group +.P +\fB +.EX +local function addGroup(name, launcher, flags) + assert(type(name) == "string") + + -- create Groups table if needed + if Groups == nil then + Groups = {} + end + + local new_group = {} + new_group.name = name + new_group.Entries = {} + if launcher ~= nil then + new_group.Launcher = launcher + end + if flags ~= nil then + new_group.Flags = flags + end + + table.insert(Groups, new_group) + return new_group +end +\fR + + +.SS "Adding Entries" +For a group to be valid, it must contain at least one valid entry\. Entries are to be inserted in the \fBEntries\fR key of a group table\. \fBEntries\fR is an array of tables\. Each entry is a table containing the following keys: +.IP "Mandatory keys:" +.IP "\[ci]" 4 +\fBname\fR \fIstring\fR \- a name for the entry +.IP "" 0 + +.IP "Optional keys:" +.IP "\[ci]" 4 +\fBpath\fR \fIstring\fR \- a path to the file associated with this entry\. If not set, the path is the same as the name +.IP "" 0 + +.IP "" 0 +.P +Here is a helper function you can use in your config to add an entry to a group +.P +\fB +.EX +local function addEntry(parentGroup, name, path) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(name) == "string") + + local new_entry = {} + new_entry.name = name + if path ~= nil then + new_entry.path = path + end + + table.insert(parentGroup.Entries, new_entry) + return new_entry +end +\fR + +.IP "" 0 +.SS "Adding Entries using lfs module" +It is recommended to install the lua\-filesystem \fIhttps://github\.com/lunarmodules/luafilesystem\fR module and use it to add entries more efficiently\. This example can be used to descend into a directory and add files matching a \fBstring\.match\fR pattern to a group\. +\fB +.EX +local lfs = require "lfs" + +local function addEntries(parentGroup, startDir, filePattern, recursive) + -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(startDir) == "string") + assert(type(filePattern) == "string") + + for file in lfs.dir(startDir) do + local fullFilePath = startDir .. "/" .. file + if file ~= "." and file ~= ".." then + -- descend into subdirectory if recursive is set to true + if lfs.attributes(fullFilePath).mode == "directory" and recursive == true then + addEntries(parentGroup, fullFilePath, filePattern, recursive) + elseif lfs.attributes(fullFilePath).mode == "file" then + if string.match(file, filePattern) then + table.insert(parentGroup.Entries, { + name = file, + path = fullFilePath + }) + end + end + end + end +end +\fR + +.SS "Settings" +Settings can be set via the \fBSettings\fR global variable\. +.P .nf -autoAlias on - -# Adding a Group of Various Applications +.BI "Settings = {} " +.BI "Settings\.sort = <boolean>" -addGroup Applications -addName GIMP /usr/bin/gimp Applications -addName Chromium /usr/bin/chromium-browser Applications -addName Thunderbird /usr/bin/thunderbird Applications +\fBsort\fR will sort entries of each group in alphabetical order (though not the list of groups)\. \fBsort\fR is true by default\. -# Adding a Videos Group that contains mp4 files - -addGroup Videos -setLauncher Videos /usr/bin/vlc -add /home/john/Videos/*mp4 Videos - -.fi -# Adding a Pictures Group that contains only jpg and png files as well as all files from an external drive and a single desktop wallpaper -.nf - -addGroup Pictures -setLauncher Pictures /usr/bin/sxiv -setFlags Pictures "-s f" -add /home/john/Pictures/*jpg Pictures -add /home/john/Pictures/*png Pictures -addR "/mnt/External_Drive/Johns Photos/*" Pictures -addName "My Desktop Background" "/mnt/External_Drive/desktop wallpaper.jpg" Pictures +.SH EXAMPLE +Here is an example \fBconfig\.lua\fR which creates a Music, Pictures, and, Videos group, and adds all the files from the user's Music, Pictures, and Videos home folders to each group\. + +\fB +.EX +local lfs = require "lfs" + +local function addGroup(name, launcher, flags) + assert(type(name) == "string") + + -- create Groups table if needed + if Groups == nil then + Groups = {} + end + + local new_group = {} + new_group.name = name + new_group.Entries = {} + if launcher ~= nil then + new_group.Launcher = launcher + end + if flags ~= nil then + new_group.Flags = flags + end + + table.insert(Groups, new_group) + return new_group +end + +local function addEntries(parentGroup, startDir, filePattern, recursive) + -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default) + assert(type(parentGroup) == "table") + assert(type(parentGroup.Entries) == "table") + assert(type(startDir) == "string") + assert(type(filePattern) == "string") + + for file in lfs.dir(startDir) do + local fullFilePath = startDir .. "/" .. file + if file ~= "." and file ~= ".." then + -- descend into subdirectory if recursive is set to true + if lfs.attributes(fullFilePath).mode == "directory" and recursive == true then + addEntries(parentGroup, fullFilePath, filePattern, recursive) + elseif lfs.attributes(fullFilePath).mode == "file" then + if string.match(file, filePattern) then + table.insert(parentGroup.Entries, { + name = file, + path = fullFilePath + }) + end + end + end + end +end + +local music = addGroup("Music", "xdg-open") +addEntries(music, os.getenv("HOME") .. "/Music", ".*", true) + +local pictures = addGroup("Pictures", "xdg-open") +addEntries(pictures, os.getenv("HOME") .. "/Pictures", ".*", true) + +local videos = addGroup("Videos", "xdg-open") +addEntries(videos, os.getenv("HOME") .. "/Videos", ".*", true) +\fR .fi .SH SEE ALSO diff --git a/man/terminal-media-launcher.1 b/man/terminal-media-launcher.1 index c25b99a..14d4c14 100644 --- a/man/terminal-media-launcher.1 +++ b/man/terminal-media-launcher.1 @@ -71,13 +71,13 @@ Exit the program Any other input will jump to the next matching group/entry that begins with the input character, if one exists (ex. hitting 'f' while focused on the entries column will jump to the next entry that starts with an 'f' unless there is no entry that starts with an 'f'). .SH FILES -\fB~/.config/terminal-media-launcher/config (or ~/.terminal-media-launcher/config)\fR +\fB~/.config/terminal-media-launcher/config.lua (or ~/.terminal-media-launcher/config.lua)\fR .RS When starting, terminal-media-launcher looks for a configuration file in the following order: - 1. ~/.config/terminal-media-launcher/config + 1. ~/.config/terminal-media-launcher/config.lua - 2. ~/.terminal-media-launcher/config + 2. ~/.terminal-media-launcher/config.lua You can specify a custom path using the -c option. For documentation of the config file, see \fBterminal-media-launcher-config\fR(5). .RE diff --git a/spec/terminal-media-launcher.spec.in b/spec/terminal-media-launcher.spec.in index 238282e..f25240b 100644 --- a/spec/terminal-media-launcher.spec.in +++ b/spec/terminal-media-launcher.spec.in @@ -8,6 +8,7 @@ URL: https://github.com/lshprung/terminal-media-launcher Source0: %{name}-%{version}.tar.gz Requires: glibc >= 2.7, ncurses >= 6 +Recommends: lua-filesystem %description Terminal Media Launcher is a command line utility to help streamline @@ -38,7 +39,6 @@ rm -rf $RPM_BUILD_ROOT %{_docdir}/terminal-media-launcher/windows_compile_instructions.md %{_mandir}/man1/terminal-media-launcher.1.gz %{_mandir}/man5/terminal-media-launcher-config.5.gz -%{_mandir}/man5/terminal-media-launcher-config.5.gz %{_datadir}/applications/terminal-media-launcher.desktop %{_datadir}/pixmaps/terminal-media-launcher.svg diff --git a/src/Makefile.am b/src/Makefile.am index 80a550c..7fbb284 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,8 +3,8 @@ EXTRA_DIST = include unix windows bin_PROGRAMS = terminal-media-launcher terminal_media_launcher_SOURCES = cache.c draw.c read_cfg.c group.c entry.c $(PLATFORM)/cache.c $(PLATFORM)/draw.c $(PLATFORM)/read_cfg.c -terminal_media_launcher_LDADD = @CURSES_LIBS@ -terminal_media_launcher_CPPFLAGS = @CURSES_CFLAGS@ +terminal_media_launcher_LDADD = @CURSES_LIBS@ @LUA_LIB@ +terminal_media_launcher_CPPFLAGS = @CURSES_CFLAGS@ @LUA_INCLUDE@ if WINDOWS PLATFORM=windows @@ -1,3 +1,4 @@ +#include <assert.h> #include <getopt.h> #include <stdbool.h> #include <stdio.h> @@ -40,9 +41,7 @@ int g_hover = 0; int *e_hover; int true_hover = 0; //0 = hovering on groups, 1 = hovering on entries GROUP **g; -ENTRY **e; int g_count; -int e_count; int g_offset = 0; int *e_offset; @@ -70,15 +69,23 @@ int main(int argc, char **argv){ //Fill Groups //read the contents of the cfg file; print help message if invalid - if(!cfg_interp(cfg_path)){ + g = cfg_interp(cfg_path, &g_count); + if(g == NULL) { print_help(argv[0]); return 1; } + // DEBUG + //for(i = 0; i < g_count; ++i) { + // group_debug(g[i]); + //} + //return 0; + /* //Remove Empty Groups from the Array clean_groups(); g = get_groups(); //retrieve results of cfg_interp g_count = get_gcount(); //retrieve number of groups in g (only do this after removing empty groups) + */ //check that there are is at least one valid group if(g_count == 0){ @@ -90,7 +97,7 @@ int main(int argc, char **argv){ e_hover = calloc(g_count, sizeof(int)); e_offset = calloc(g_count, sizeof(int)); - //load cached data + // load cached data load_cache(g_count, &g_hover, &e_hover, &e_offset, &true_hover, cfg_path); //reopen stdout for drawing menu @@ -125,6 +132,7 @@ int main(int argc, char **argv){ update_display(true); //drawing is done, now run a while loop to receive input (ESC ends this loop) + input = 0; while(input != 27){ input = getch(); @@ -151,12 +159,12 @@ int main(int argc, char **argv){ case KEY_NPAGE: //case KEY_SDOWN: - trav_col((true_hover ? e_count : g_count)-1); + trav_col((true_hover ? get_ecount(g[g_hover]) : g_count)-1); break; case KEY_F(3): //jump to random group/entry - trav_col(rand() % (true_hover ? e_count : g_count)); + trav_col(rand() % (true_hover ? get_ecount(g[g_hover]) : g_count)); break; case KEY_F(5): @@ -207,7 +215,7 @@ bool *handle_args(int argc, char **argv, char **cfg_path){ {"version", no_argument, NULL, 'v'}, {0, 0, 0, 0} }; - bool *flags_set = calloc(FLAG_COUNT, sizeof(bool)); + bool *flags_set = calloc(FLAG_COUNT+1, sizeof(bool)); int i = 0; while(opt != -1){ @@ -332,8 +340,9 @@ void fill_col(int mode){ //mode 1 = entry int i; + ENTRY **entries = get_gentries(g[g_hover]); WINDOW *col = (mode ? entry_win : group_win); - int count = (mode ? e_count : g_count); + int count = (mode ? get_ecount(g[g_hover]) : g_count); int offset = (mode ? e_offset[g_hover] : g_offset); int max_len = getmaxx(col)-2; //longest possible string length that can be displayed in the window int ycoord = 1; @@ -342,10 +351,10 @@ void fill_col(int mode){ for(i = 0+offset; i < count; i++){ if(ycoord >= max_y) break; //reached the bottom of the terminal window, stop drawing - name = (mode ? get_ename(e[i]) : get_gname(g[i])); + name = (mode ? get_ename(entries[i]) : get_gname(g[i])); //the name is too long, take the group to the trimming function - if(strlen(name) > max_len) name = trim_name(name, (mode ? get_epath(e[i]) : get_gname(g[i])), max_len); + if(strlen(name) > max_len) name = trim_name(name, (mode ? get_epath(entries[i]) : get_gname(g[i])), max_len); wmove(col, ycoord, 1); wprintw(col, "%s", name); ycoord++; @@ -421,8 +430,6 @@ void update_col(int mode, int y_hl, bool resize){ break; case 1: - e_count = get_ecount(g[g_hover]); - e = get_entries(get_ghead(g[g_hover]), e_count); fill_col(1); if(!resize) mvwchgat(entry_win, y_hl, 1, getmaxx(entry_win)-2, A_DIM, 1, NULL); else mvwchgat(entry_win, 1+e_hover[g_hover]-e_offset[g_hover], 1, getmaxx(entry_win)-2, A_DIM, (true_hover ? 2 : 1), NULL); @@ -464,7 +471,7 @@ void switch_col(){ void trav_col(int new_i){ int *focus = (true_hover ? &(e_hover[g_hover]) : &g_hover); //make it easy to know which column we are looking at int *offset = (true_hover ? &(e_offset[g_hover]) : &g_offset); - int count = (true_hover ? e_count : g_count); + int count = (true_hover ? get_ecount(g[g_hover]) : g_count); int max_hl = HEIGHT-(3+GAP_SIZE); //for some reason, this works int min_hl = 5; int oob_flag = 0; //0 = none, 1 = bottom, 2 = top @@ -478,7 +485,6 @@ void trav_col(int new_i){ mvwchgat(group_win, 1+g_hover-g_offset, 1, getmaxx(group_win)-2, A_NORMAL, 0, NULL); *focus = new_i; - //check offsets relating to new highlight, make sure highlight did not go oob while(*focus-*offset+5 > max_hl){ (*offset)++; @@ -512,16 +518,17 @@ void trav_col(int new_i){ } int locateChar(char input){ + ENTRY **entries = get_gentries(g[g_hover]); int location = (true_hover ? e_hover[g_hover] : g_hover); - bool fold_case = get_case_sensitivity(); + bool fold_case = true; char first_char; int i; if(fold_case && input >= 97 && input <= 122) input -= 32; if(true_hover){ //hovering on entries - for(i = location+1; i < e_count; i++){ - first_char = get_ename(e[i])[0]; + for(i = location+1; i < get_ecount(g[g_hover]); i++){ + first_char = get_ename(entries[i])[0]; if(fold_case && first_char >= 97 && first_char <= 122) first_char -= 32; if(input == first_char){ location = i; @@ -545,33 +552,25 @@ int locateChar(char input){ } char *get_launch(){ + ENTRY **entries = get_gentries(g[g_hover]); char *program = get_gprog(g[g_hover]); char *flags = get_gflags(g[g_hover]); - char *path = get_epath(e[e_hover[g_hover]]); - bool quotes = get_gquotes(g[g_hover]); - char *full_command = malloc(sizeof(char) * BUF_LEN); - - full_command[0] = '\0'; + char *path = get_epath(entries[e_hover[g_hover]]); + char *full_command = calloc(BUF_LEN, sizeof(char)); //if the entry is an executable file (doesn't have a launcher) if(!(strcmp(program, "./"))){ - strcat(full_command, "\""); strcat(full_command, path); - strcat(full_command, "\""); } else{ - if(quotes) strcat(full_command, "\""); strcat(full_command, program); - if(quotes) strcat(full_command, "\""); if(flags[0] !='\0'){ strcat(full_command, " "); strcat(full_command, flags); } strcat(full_command, " "); - strcat(full_command, "\""); strcat(full_command, path); - strcat(full_command, "\""); } return full_command; diff --git a/src/entry.c b/src/entry.c index 6dc5699..dceb424 100644 --- a/src/entry.c +++ b/src/entry.c @@ -7,18 +7,15 @@ #include <unistd.h> #include "include/entry.h" -#include "include/group.h" #include "include/read_cfg.h" typedef struct entry{ char name[BUF_LEN]; char path[BUF_LEN]; bool path_force; - bool hidden; - struct entry *next; } ENTRY; -ENTRY *create_entry(char *new_name, char *new_path, bool force){ +ENTRY *create_entry(const char *new_name, const char *new_path, const bool force){ ENTRY *new; new = malloc(sizeof(ENTRY)); @@ -26,85 +23,15 @@ ENTRY *create_entry(char *new_name, char *new_path, bool force){ strcpy(new->name, new_name); strcpy(new->path, new_path); new->path_force = force; - new->hidden = false; - new->next = NULL; return new; } -void entry_rm(ENTRY *e, ENTRY *prev){ - assert(e != NULL); - if(prev != NULL) prev->next = e->next; //maintain linked structure - free(e); -} - -void clear_entries(ENTRY *head){ - ENTRY *temp; - - while(head != NULL){ - temp = head; - head = head->next; - free(temp); - } - - return; -} - -//returns 0 if in the middle, 1 if new head, 2 if new tail, or 3 if both new head and tail -//TODO this is kind of a stupid way of handling things -int entry_add(ENTRY *head, ENTRY *tail, ENTRY *add){ - assert(add != NULL); - ENTRY *ahead; - - //Empty group (no need to sort) - if(head == NULL) return 3; - - //add is the new tail - if(!get_sort() || strcmp(tail->name, add->name) <= 0){ - tail->next = add; - return 2; - } - - //add is the new head - if(strcmp(add->name, head->name) <= 0){ - add->next = head; - return 1; - } - - ahead = head->next; - - while(ahead != NULL){ - if(strcmp(head->name, add->name) <= 0 && strcmp(add->name, ahead->name) <= 0) break; - head = head->next; - ahead = ahead->next; - } - - head->next = add; - add->next = ahead; - - return 0; -} - -ENTRY **get_entries(ENTRY *head, int count){ - ENTRY **arr = malloc(sizeof(ENTRY *) * count); - ENTRY *trav = head; - int i = 0; - - while(i < count){ - if(!trav->hidden){ - arr[i] = trav; - i++; - } - trav = trav->next; - } - - return arr; -} - char *get_ename(ENTRY *e){ assert(e != NULL); return e->name; } + char *get_epath(ENTRY *e){ assert(e != NULL); return e->path; @@ -115,11 +42,7 @@ bool get_eforce(ENTRY *e){ return e->path_force; } -void set_hide(ENTRY *e, bool status){ - assert(e != NULL); - e->hidden = true; -} - +/* void entry_debug(ENTRY *trav){ while(trav != NULL){ @@ -129,3 +52,4 @@ void entry_debug(ENTRY *trav){ return; } +*/ diff --git a/src/group.c b/src/group.c index 0c0d5a0..95d8605 100644 --- a/src/group.c +++ b/src/group.c @@ -13,166 +13,27 @@ typedef struct group{ char name[BUF_LEN]; char program[BUF_LEN]; char flags[BUF_LEN]; - struct entry *head; - struct entry *tail; - struct group *next; + struct entry **entries; int entry_count; - bool launcher_quotes; //set by a group option whether or not the launcher should be wrapped by quotes + //bool launcher_quotes; //set by a group option whether or not the launcher should be wrapped by quotes } GROUP; -GROUP *groups_head; -GROUP *gp; //pointer to remember last group that was looked at int group_count = 0; int total_count = 0; -GROUP *create_group(char *new_name){ +GROUP *create_group(const char *new_name, const int entry_count){ GROUP *new = malloc(sizeof(GROUP)); strcpy(new->name, new_name); //by default, group name is equivalent to the path strcpy(new->program, "./"); //by default, launch an entry by executing it new->flags[0] = '\0'; //by default, no command line flags - new->head = NULL; - new->tail = NULL; - new->next = NULL; - new->entry_count = 0; - new->launcher_quotes = true; + new->entries = malloc(sizeof(ENTRY *) * entry_count); + new->entry_count = entry_count; group_count++; return new; } -//add an entry to a group or add a new empty group -//FIXME maybe make this function part of a seperate file to handle a tree (AVL?) -//for now, simple linked list implementation -void group_add(char *gname, ENTRY *addme){ - int i; - GROUP *new; - GROUP *last = NULL; //last element in an existing group list (NULL to start) - - //only adding a new group - if(addme == NULL){ - gp = groups_head; - while(gp != NULL){ - if(!(strcmp(gp->name, gname))){ - printf("config error: %s is already a group!\n", gname); - return; - } - - last = gp; - gp = gp->next; - } - } - - //The previous group is not the same as the new group to add to - if(!(gp != NULL && (!(strcmp(gp->name, gname))))){ - gp = groups_head; - while(gp != NULL){ - //gname matches groups[i]'s name, add entry here - if(!(strcmp(gp->name, gname))) break; - - last = gp; - gp = gp->next; - } - } - - //was unable to find a matching existing group - //need to create new group to insert the entry into - if(gp == NULL){ - new = create_group(gname); - - //first group - if(last == NULL) groups_head = new; - - //add to the end of the groups - else last->next = new; - - gp = new; - } - - //add the entry to the list of entries in the group - if(addme != NULL){ - i = entry_add(gp->head, gp->tail, addme); - switch(i){ - case 1: - gp->head = addme; - break; - - case 2: - gp->tail = addme; - break; - - case 3: - gp->head = addme; - gp->tail = addme; - break; - - } - - gp->entry_count++; - total_count++; - } - - return; -} - -void group_rm(GROUP *g){ - - clear_entries(g->head); - - free(g); - group_count--; - return; -} - -void clean_groups(){ - GROUP *dummy_head; - GROUP *trav; - GROUP *hold; - - if(group_count == 0){ - printf("Error: no groups! "); - refer_to_doc(); - exit(0); - } - - else{ - dummy_head = create_group("dummy"); - dummy_head->next = groups_head; - trav = dummy_head; - - while(trav != NULL){ - //found empty group for removal - if(trav->next != NULL && trav->next->entry_count < 1){ - printf("Omitting empty group \"%s\"\n", trav->next->name); - hold = trav->next; - trav->next = trav->next->next; - group_rm(hold); - } - else trav = trav->next; - } - } - - //ensure groups->head is still correct - groups_head = dummy_head->next; - group_rm(dummy_head); - return; - -} - - -GROUP **get_groups(){ - GROUP **arr = malloc(sizeof(GROUP *) * group_count); - GROUP *trav = groups_head; - int i; - - for(i = 0; i < group_count; i++){ - arr[i] = trav; - trav = trav->next; - } - - return arr; -} - char *get_gname(GROUP *g){ assert(g != NULL); return g->name; @@ -183,7 +44,7 @@ char *get_gprog(GROUP *g){ return g->program; } -void set_gprog(GROUP *g, char *p){ +void set_gprog(GROUP *g, const char *p){ assert(g != NULL); strcpy(g->program, p); return; @@ -194,14 +55,19 @@ char *get_gflags(GROUP *g){ return g->flags; } -void set_gflags(GROUP *g, char *p){ +void set_gflags(GROUP *g, const char *p){ assert(g != NULL); strcpy(g->flags, p); } -ENTRY *get_ghead(GROUP *g){ +ENTRY **get_gentries(GROUP *g) { + assert(g != NULL); + return g->entries; +} + +void set_gentry(GROUP *g, int entry_index, ENTRY *new_entry) { assert(g != NULL); - return g->head; + g->entries[entry_index] = new_entry; } int get_ecount(GROUP *g){ @@ -214,27 +80,9 @@ void set_ecount(GROUP *g, int new_count){ g->entry_count = new_count; } -void set_gquotes(GROUP *g, bool b){ - assert(g != NULL); - g->launcher_quotes = b; -} - -bool get_gquotes(GROUP *g){ - return g->launcher_quotes; -} - -int get_gcount(){ - return group_count; -} - -void group_debug(){ - GROUP *trav = groups_head; - - while(trav != NULL){ - entry_debug(trav->head); - printf("\tfrom group %s\n", trav->name); - trav = trav->next; - } - - return; +void group_debug(GROUP *g){ + printf("Entering group: %s\n", get_gname(g)); + printf("\tProgram: %s\n", get_gprog(g)); + printf("\tFlags: %s\n", get_gflags(g)); + printf("\tEntry Count: %d\n", get_ecount(g)); } diff --git a/src/include/cache.h b/src/include/cache.h index 894e4e1..701691f 100644 --- a/src/include/cache.h +++ b/src/include/cache.h @@ -1,6 +1,8 @@ #ifndef CACHE_H #define CACHE_H +#include <stdbool.h> + void save_to_cache(int g_count, int g_hover, int *e_hover, int *e_offset, int true_hover, char *cfg_name); void load_cache(int g_count, int *g_hover, int **e_hover, int **e_offset, int *true_hover, char *new_cfg_name); diff --git a/src/include/draw.h b/src/include/draw.h index 387ced3..022abe5 100644 --- a/src/include/draw.h +++ b/src/include/draw.h @@ -5,11 +5,14 @@ #define BUF_LEN 1024 +// currently selected group extern int g_hover; +// array of currently selected entries for each group extern int *e_hover; +// array of groups (as loaded from the config) extern struct group **g; -extern struct entry **e; +// returns the command to run for the current entry char *get_launch(); //functions that differ between os diff --git a/src/include/entry.h b/src/include/entry.h index 51e43ca..a46dc46 100644 --- a/src/include/entry.h +++ b/src/include/entry.h @@ -1,17 +1,11 @@ +#include <stdbool.h> + #ifndef ENTRY_H #define ENTRY_H typedef struct entry ENTRY; -ENTRY *create_entry(char *new_name, char *new_path, bool force); - -void entry_rm(ENTRY *e, ENTRY *prev); - -void clear_entries(ENTRY *head); - -int entry_add(ENTRY *head, ENTRY *tail, ENTRY *add); - -ENTRY **get_entries(ENTRY *head, int count); +ENTRY *create_entry(const char *new_name, const char *new_path, const bool force); char *get_ename(ENTRY *e); @@ -19,10 +13,4 @@ char *get_epath(ENTRY *e); bool get_eforce(ENTRY *e); -void set_hide(ENTRY *e, bool status); - -bool get_hide(ENTRY *e); - -void entry_debug(ENTRY *trav); - #endif diff --git a/src/include/group.h b/src/include/group.h index ab6f409..bad33a5 100644 --- a/src/include/group.h +++ b/src/include/group.h @@ -1,40 +1,33 @@ #ifndef GROUP_H #define GROUP_H -typedef struct group GROUP; - -GROUP *create_group(char *new_name); +#include <stdbool.h> -void group_add(char *gname, ENTRY *addme); +#include "entry.h" -void group_rm(GROUP *g); - -void clean_groups(); //remove empty groups from linked list +typedef struct group GROUP; -GROUP **get_groups(); +GROUP *create_group(const char *new_name, const int entry_count); char *get_gname(GROUP *g); char *get_gprog(GROUP *g); -void set_gprog(GROUP *g, char *p); +void set_gprog(GROUP *g, const char *p); char *get_gflags(GROUP *g); -void set_gflags(GROUP *g, char *p); +void set_gflags(GROUP *g, const char *p); -ENTRY *get_ghead(GROUP *g); +ENTRY **get_gentries(GROUP *g); + +void set_gentry(GROUP *g, int entry_index, ENTRY *new_entry); int get_ecount(GROUP *g); void set_ecount(GROUP *g, int new_count); //for use in hiding entries -void set_gquotes(GROUP *g, bool b); - -bool get_gquotes(GROUP *g); - -int get_gcount(); - -void group_debug(); //debug function to output all groups +// print all group and entry information +void group_debug(GROUP *g); #endif diff --git a/src/include/read_cfg.h b/src/include/read_cfg.h index df3e5bd..1337e32 100644 --- a/src/include/read_cfg.h +++ b/src/include/read_cfg.h @@ -1,23 +1,18 @@ #ifndef READ_CFG_H #define READ_CFG_H +#include <stdbool.h> + +#include "group.h" + #define BUF_LEN 1024 -bool cfg_interp(char *path); -bool get_sort(); -bool get_case_sensitivity(); +GROUP **cfg_interp(char *path, int *group_count); void refer_to_doc(); -void addme(char *path, char *group, bool force, char *name); -int search_ch(char *str, char c); -int search_last_ch(char *str, char c); -int wild_cmp(char *wild, char *literal); -char *strip_quotes(char *str); -void error_mes(int ln, char *message); //functions that differ by os extern char sep; char *find_config(); void mkconfig_wizard(char *path); -void handle_fname(char *path, char *group, bool recurs, bool force, char *name, int ln); #endif diff --git a/src/read_cfg.c b/src/read_cfg.c index f757d6b..6c334ac 100644 --- a/src/read_cfg.c +++ b/src/read_cfg.c @@ -1,4 +1,7 @@ #include <assert.h> +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -19,87 +22,87 @@ #define OPTION_CNT 14 //private -void check_line(char *buffer, char **options, int ln); -int check_option(char *arg, char **options); -char *autoAlias(char *path); - -//turn on or off sorting (A-Z); On by default +//void check_line(char *buffer, char **options, int ln); +//int check_option(char *arg, char **options); +void get_settings(lua_State *L); // gets settings from Settings global variable +int get_group_count(lua_State *L); // counts the number of valid groups +int get_entry_count(lua_State *L); // counts the number of valid entries for a group +void add_groups(lua_State *L, GROUP ***g); +void add_entries(lua_State *L, GROUP *g); +void stack_debug(lua_State *L); +void table_debug(lua_State *L, int print_depth); + +//turn on or off sorting for entries (A-Z); On by default +// TODO allow specifying whether to sort groups or entries (or both, or none) bool sort = true; -//set to true to automatically try to create a human readable name for an entry -bool hr = false; - -//turn foldCase (insensitive case searching) on or off; On by default -bool fold_case = true; - //return false if invalid path -bool cfg_interp(char *path){ +GROUP **cfg_interp(char *path, int *group_count){ FILE *fp; char buffer[BUF_LEN]; GROUP **g; - ENTRY **e; - int count; - int e_count; - int i=0; + const char *group_name; + //ENTRY **e; + int i; int j; + // check if file path exists fp = fopen(path, "r"); if(fp == NULL){ printf("Error: Invalid Configuration Path \"%s\"\n", path); - return false; + return NULL; } + fclose(fp); - //build the options array - char **options = malloc(sizeof(char *) * OPTION_CNT); - options[0] = "add"; - options[1] = "addF"; - options[2] = "addGroup"; - options[3] = "addName"; - options[4] = "addNameF"; - options[5] = "addR"; - options[6] = "autoAlias"; - options[7] = "foldCase"; - options[8] = "hide"; - options[9] = "hideFile"; - options[10] = "setFlags"; - options[11] = "setLauncher"; - options[12] = "setLauncherRaw"; - options[13] = "sort"; - - //Read each line of "config" - while(fgets(buffer, BUF_LEN, fp)){ - i++; - check_line(buffer, options, i); + // load lua configuration + lua_State *L = luaL_newstate(); + // new lua stack + luaL_openlibs(L); // allow for standard library to be used + int config_load_status = luaL_dofile(L, path); + if(config_load_status != 0) { + printf("Error: could not load configuration \"%s\"\n", path); + lua_error(L); + exit(1); } - //cleanup - free(options); - - /* - //DEBUG: test to see if the list was added to properly - g = get_groups(); - count = get_gcount(); - for(i = 0; i < count; i++){ - printf("Looking at group %s\n", get_gname(g[i])); - e_count = get_ecount(g[i]); - e = get_entries(get_ghead(g[i]), e_count); - for(j = 0; j < e_count; j++){ - printf("\t%s\n", get_ename(e[j])); - } + // set up base configuration variables + // TODO set helper variables and functions (e.g., so that Groups table doesn't need to be manually created in the user's config) + //lua_newtable(L); + //lua_setglobal(L, "Groups"); + //lua_pcall(L, 0, 0, 0); + + // open Settings table + lua_getglobal(L, "Settings"); + // stack now contains: -1 => Settings + if(lua_type(L, -1) == LUA_TTABLE) { + // parse settings + get_settings(L); + } + lua_pop(L, 1); + // empty stack + + // open Groups table + lua_getglobal(L, "Groups"); + // stack now contains: -1 => Groups + if(lua_type(L, -1) != LUA_TTABLE) { + printf("Error in config: 'Groups' should be Table, is actually %s\n", lua_typename(L, lua_type(L, -1))); + exit(1); } - //END DEBUG - */ - - fclose(fp); - return true; -} -bool get_sort(){ - return sort; -} + // create the group array + *group_count = get_group_count(L); + if(*group_count <= 0) { + printf("Error: No Groups!\n"); + g = NULL; + } + else { + g = malloc(sizeof(GROUP *) * (*group_count)); -bool get_case_sensitivity(){ - return fold_case; + // add each group (which also adds each entry to each group) + add_groups(L, &g); + } + lua_close(L); + return g; } void refer_to_doc(){ @@ -107,409 +110,298 @@ void refer_to_doc(){ return; } -void addme(char *path, char *group, bool force, char *name){ - ENTRY *new; - char auto_name[BUF_LEN]; - - //check if a name was given as argument - if(name != NULL){ - //strip quotes from the name - name = strip_quotes(name); - new = create_entry(name, path, force); - } - - //check if autoAlias is on. If it is, go to the autoAlias function - else if(hr){ - strcpy(auto_name, autoAlias(path)); - new = create_entry(auto_name, path, force); - } - - else new = create_entry(path, path, force); - if(new != NULL) group_add(group, new); - - return; -} +void get_settings(lua_State *L) { + // stack now contains: -1 => table -int search_ch(char *str, char c){ - int i = 0; + bool *setting_vars[] = { + &sort + }; - while(str[i] != '\0'){ - if(str[i] == c) return i; - i++; - } + char *setting_strings[] = { + "sort" + }; - return -1; -} + int count = 1; + int i; -int search_last_ch(char *str, char c){ - int i = 0; - int last_i = -1; + // looking at table Settings - while(str[i] != '\0'){ - if(str[i] == c) last_i = i; - i++; + // check if autoAlias is set + for(i = 0; i < count; ++i) { + lua_pushstring(L, setting_strings[i]); + // stack now contains: -1 => setting_string; -2 => table + lua_gettable(L, -2); + // stack now contains: -1 => string_value; -2 => table + if(lua_type(L, -1) == LUA_TBOOLEAN) { + *(setting_vars[i]) = lua_toboolean(L, -1); + } + lua_pop(L, 1); + // stack now contains: -1 => table } - - return last_i; } -//return 0 if match, 1 if not -//TODO only supports one wildcard per entry -int wild_cmp(char *wild, char *literal){ - int i; - - while(*wild != '\0'){ - //traverse until wildcard - if(*wild != '*'){ - if(*wild != *literal) return 1; - wild++; - literal++; - } - - //found wildcard, find the end of both names and comapre from the back - else{ - i = 0; - wild++; - while(*wild != '\0'){ - i++; - wild++; - } - while(*literal != '\0'){ - literal++; - } - - while(i > 0){ - wild--; - literal--; - if(*wild != *literal) return 1; - i--; +int get_group_count(lua_State *L) { + int output = 0; + + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + while (lua_next(L, -2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + if(lua_type(L, -1) == LUA_TNUMBER && lua_type(L, -2) == LUA_TTABLE) { + lua_pushstring(L, "name"); + // stack now contains: -1 => "name"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => name; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) == LUA_TSTRING) { + // check that the Entries table for this group is not empty + lua_pushstring(L, "Entries"); + // stack now contains: -1 => "Entries"; -2 => name; -3 => key; -4 => value; -5 => key; -6 => table + lua_gettable(L, -4); + // stack now contains: -1 => Entries; -2 => name; -3 => key; -4 => value; -5 => key; -6 => table + if(lua_type(L, -1) == LUA_TTABLE && get_entry_count(L) > 0) + ++output; + lua_pop(L, 1); + // stack now contains: -1 => name; -2 => key; -3 => value; -4 => key; -5 => table } - - return 0; + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table } + lua_pop(L, 2); + // stack now contains: -1 => key; -2 => table } + // stack now contains: -1 => table (when lua_next returns 0 it pops the key + // but does not push anything.) - return 0; + return output; } - -char *strip_quotes(char *str){ - char *stripped_str = malloc(sizeof(char) * BUF_LEN); - - if(str[0] == '"'){ - stripped_str = &str[1]; - stripped_str[strlen(stripped_str) - 1] = '\0'; - return stripped_str; +int get_entry_count(lua_State *L) { + int output = 0; + + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + while (lua_next(L, -2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + if(lua_type(L, -1) == LUA_TNUMBER && lua_type(L, -2) == LUA_TTABLE) { + // check that the entry has a valid name + lua_pushstring(L, "name"); + // stack now contains: -1 => "name"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => name; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) == LUA_TSTRING) ++output; + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + } + lua_pop(L, 2); + // stack now contains: -1 => key; -2 => table } + // stack now contains: -1 => table (when lua_next returns 0 it pops the key + // but does not push anything.) - return str; -} - -void error_mes(int ln, char *message){ - - assert(message != NULL); - - printf("Configuration File Error:\nOn line %d: %s\n\n", ln, message); - - return; + return output; } -//TODO add support for "addR" recursive adding (still needs work...) -//TODO add support for "alias" option -//TODO add support for "hide" option -void check_line(char *buffer, char **options, int ln){ - char *delims = " \t\n"; - char *tok = strtok(buffer, delims); - char args[MAX_ARGS][BUF_LEN]; - GROUP **g; - ENTRY **e; - char *tok_p; - char *arg_p; - int g_count; - int e_count; - int search_res; - int i, j; - char *error_p; //helper for complex error messages - - //ensure line is not blank or commented out - if(tok != NULL && tok[0] != '#' && tok[0] != '\0'){ - //initialize args to 0 - for(i = 0; i < MAX_ARGS; i++){ - args[i][0] = '\0'; - } - - i = 0; - //record all arguments in the line - while(tok != NULL){ - if(i >= MAX_ARGS){ - error_mes(ln, "Too many arguments"); - return; - } - strcpy(args[i], tok); - //handle if an argument has spaces and is wrapped in quotes - if(tok[0] == '"'){ - arg_p = &args[i][0]; - tok_p = &tok[1]; - - while(*tok_p != '"'){ - switch(*tok_p){ - - - case '\0': - tok = strtok(NULL, delims); - tok_p = &tok[0]; - *arg_p = ' '; - arg_p++; - break; - - case '\\': - tok_p++; - - default: - *arg_p = *tok_p; - tok_p++; - arg_p++; - - } +void add_groups(lua_State *L, GROUP ***g) { + const char *group_name; + int group_table_stack_index; // index of Groups.TABLE_NAME + int entry_table_stack_index; // index of Groups.TABLE_NAME.Entries + int entry_count; + char sort_cmd_lua[BUF_LEN]; // used to store the lua call to table.sort + int i = 1; // index in lua table + int count = 0; // index in C struct + + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + while (lua_next(L, -2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + if(lua_type(L, -1) == LUA_TNUMBER && lua_type(L, -2) == LUA_TTABLE) { + lua_pushstring(L, "name"); + // stack now contains: -1 => "name"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => name; -2 => key; -3 => value; -4 => key; -5 => table + group_name = lua_tostring(L, -1); + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + if(group_name != NULL) { + // push the Entries table on the stack (to get entry information) + lua_pushstring(L, "Entries"); + // stack now contains: -1 => "Entries"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => Entries; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) != LUA_TTABLE) { + printf("Error in config: in group '%s': 'Entries' should be Table, is actually %s\n", group_name, lua_typename(L, lua_type(L, -1))); + exit(1); } - *arg_p = '\0'; - - } - - tok = strtok(NULL, delims); - i++; - } - - //optimally check which option was specified - search_res = check_option(args[0], options); - - switch(search_res){ - - case 0: //add - //add entry(ies) to a group: first arg is the file(s), second arg is the group to add to - //TODO add sorting functionality - handle_fname(args[1], args[2], 0, 0, NULL, ln); - break; - - case 1: //addF - //force add entry to a group: first arg is the file(s), second arg is the group to add to - handle_fname(args[1], args[2], 0, 1, NULL, ln); - break; - - case 2: //addGroup - //create a new group - group_add(strip_quotes(args[1]), NULL); - break; + entry_count = get_entry_count(L); - case 3: //addName - //add entry to a group: first arg is the name, second arg is the file, and third arg is the group to add to - handle_fname(args[2], args[3], 0, 0, args[1], ln); - break; - - case 4: //addNameF - //same as addName, but with force on - handle_fname(args[2], args[3], 0, 1, args[1], ln); - break; - - case 5: //addR - //recursively add: that is, also search directories in the given path - //NOTE: experimental - handle_fname(args[1], args[2], 1, 0, NULL, ln); - break; - - case 6: //autoAlias - if(!(strcmp(args[1], "on"))) hr = true; - else if(!(strcmp(args[1], "off"))) hr = false; - break; - - case 7: //foldCase (case insensitive) - if(!(strcmp(args[1], "on"))) fold_case = true; - else if(!(strcmp(args[1], "off"))) fold_case = false; - break; - - //TODO consider having this call handle_fname instead so that '*' can be used - case 8: //hide - case 9: //hideFile - //args[2] is referring to a group - g = get_groups(); - g_count = get_gcount(); - - //look for matching existing group - for(i = 0; i < g_count; i++){ - if(!(strcmp(get_gname(g[i]), args[2]))) break; + // check that the group has at least 1 entry + if(entry_count <= 0) { + printf("Skipping empty group '%s'\n", group_name); + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table } + else { + (*g)[count] = create_group(group_name, entry_count); - if(i < g_count){ - e_count = get_ecount(g[i]); - e = get_entries(get_ghead(g[i]), e_count); - - for(j = 0; j < e_count; j++){ - if(!strcmp((search_res == 8 ? get_ename(e[j]) : get_epath(e[j])), strip_quotes(args[1]))) break; + // sort the entries if necessary + if(sort) { + sprintf(sort_cmd_lua, "table.sort(Groups[%d].Entries, function(a, b) return a.name < b.name end)", i); + luaL_dostring(L, sort_cmd_lua); } - if(j < e_count){ - set_hide(e[j], true); - set_ecount(g[i], get_ecount(g[i])-1); + // add entries to this group + add_entries(L, (*g)[count]); + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + + // set the launcher, if applicable + lua_pushstring(L, "Launcher"); + // stack now contains: -1 => "Launcher"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => Launcher; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) == LUA_TSTRING) { + set_gprog((*g)[count], lua_tostring(L, -1)); } - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "Entry \"%s\" does not exist", args[1]); - error_mes(ln, error_p); - free(error_p); + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + + // set the launcher flags, if applicable + lua_pushstring(L, "Flags"); + // stack now contains: -1 => "Flags"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => Flags; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) == LUA_TSTRING) { + set_gflags((*g)[count], lua_tostring(L, -1)); } - } + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "Group \"%s\" does not exist", args[2]); - error_mes(ln, error_p); - free(error_p); + ++count; } - break; - - case 10: //setFlags - //args[1] is referring to a group - g = get_groups(); - g_count = get_gcount(); - - //look for matching existing group - for(i = 0; i < g_count; i++){ - if(!(strcmp(get_gname(g[i]), args[1]))) break; - } - - //set a group's launcher flags (like ./program -f file for fullscreen) - //assert that a matching group was found - if(i < g_count) set_gflags(g[i], strip_quotes(args[2])); - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "Group \"%s\" does not exist", args[1]); - error_mes(ln, error_p); - free(error_p); - } - break; - - case 11: //setLauncher - case 12: //setLauncherRaw - //args[1] is referring to a group - g = get_groups(); - g_count = get_gcount(); - - //look for matching existing group - for(i = 0; i < g_count; i++){ - if(!(strcmp(get_gname(g[i]), args[1]))) break; - } - - //set a group's launcher (this requires pulling down the existing groups and finding the one that args[1] mentions) - //assert that a matching group was found - if(i < g_count){ - set_gprog(g[i], strip_quotes(args[2])); - if(search_res == 12) set_gquotes(g[i], false); //FIXME don't forget to change this line if adding more options!!! - } - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "Group \"%s\" does not exist", args[1]); - error_mes(ln, error_p); - free(error_p); - } - break; - - case 13: //sort - if(!(strcmp(args[1], "on"))) sort = true; - else if(!(strcmp(args[1], "off"))) sort = false; - break; - - default: - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "Unknown config option \"%s\"", args[0]); - error_mes(ln, error_p); - free(error_p); - + } } - + lua_pop(L, 2); + // stack now contains: -1 => key; -2 => table + ++i; } - - return; + // stack now contains: -1 => table (when lua_next returns 0 it pops the key + // but does not push anything.) + // Pop table + lua_pop(L, 1); + // Stack is now the same as it was on entry to this function } -int check_option(char *arg, char **options){ - int min = 0; - int max = OPTION_CNT-1; - int hover; - int comp_res; - - while(max - min > 1){ - hover = min + (max-min)/2; - comp_res = strcmp(arg, options[hover]); - - if(comp_res > 0) min = hover; - else if(comp_res < 0) max = hover; - else return hover; +void add_entries(lua_State *L, GROUP *g) { + const char *entry_name; + const char *entry_path; + int entry_table_stack_index; + int i = 1; // index in lua table + int count = 0; // index in C struct + + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + while (lua_next(L, -2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + if(lua_type(L, -1) == LUA_TNUMBER && lua_type(L, -2) == LUA_TTABLE) { + // check that the entry has a valid name + lua_pushstring(L, "name"); + // stack now contains: -1 => "name"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => name; -2 => key; -3 => value; -4 => key; -5 => table + entry_name = lua_tostring(L, -1); + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + + if(entry_name != NULL) { + // get this entry's path, if applicable + lua_pushstring(L, "path"); + // stack now contains: -1 => "path"; -2 => key; -3 => value; -4 => key; -5 => table + lua_gettable(L, -3); + // stack now contains: -1 => path; -2 => key; -3 => value; -4 => key; -5 => table + if(lua_type(L, -1) == LUA_TSTRING) entry_path = lua_tostring(L, -1); + else entry_path = entry_name; + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + + set_gentry(g, count, create_entry(entry_name, entry_path, true)); + ++count; + } + } + lua_pop(L, 2); + // stack now contains: -1 => key; -2 => table } - - if(max == OPTION_CNT-1 && strcmp(arg, options[max]) == 0) return max; - else if(min == 0 && strcmp(arg, options[min]) == 0) return min; - - return -1; + // stack now contains: -1 => table (when lua_next returns 0 it pops the key } +void stack_debug(lua_State *L) { + int i; -char *autoAlias(char *path){ - char *hr_name = malloc(sizeof(char) * BUF_LEN); - char *p = hr_name; - char *rpath; //necessary so as not to touch the actual path - char *last_dot = NULL; //used to trim the file extension (if there is one) - bool stop = false; //stop when you don't want to add a series of chars to the output - - //get to the relative path name - rpath = strrchr(path, sep); - if(rpath == NULL) rpath = path; - else rpath++; - - while(*rpath != '\0'){ - switch(*rpath){ - case '(': - stop = true; - break; - - case ')': - stop = false; - break; - - case '-': - case '_': - if(*(p-1) != ' ' && !stop){ - *p = ' '; - *p++; - } - break; + printf("DEBUGGING STACK:\n"); + for(i = 1; i <= lua_gettop(L); ++i) { + printf("\t%d - %s", i, lua_typename(L, lua_type(L, i))); - case ' ': - if(*(p-1) != ' ' && !stop){ - *p = *rpath; - *p++; - } + switch(lua_type(L, i)) { + case LUA_TSTRING: + printf(" - %s\n", lua_tostring(L, i)); break; - - case '.': - last_dot = p; + + //case LUA_TTABLE: + // printf("\n"); + // table_debug(L, 2); + // break; default: - if(!stop){ - *p = *rpath; - *p++; - } + printf("\n"); } - *rpath++; } - - //close the name - if(last_dot != NULL) *last_dot = '\0'; - else if(*path == '"') *(p-1) = '\0'; //close early to avoid including closing quote - else *p = '\0'; - - return hr_name; } +// FIXME WIP debugging function +void table_debug(lua_State *L, int print_depth) { + int i; - + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + while (lua_next(L, -2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + const char *key = lua_tostring(L, -1); + const char *value = lua_typename(L, lua_type(L, -2)); + for(i = 0; i < print_depth; ++i) { + printf("\t"); + } + printf("%s => %s", key, value); + if(lua_type(L, -2) == LUA_TSTRING) { + printf(" - %s", lua_tostring(L, -2)); + } + printf("\n"); + // pop value + copy of key, leaving original key + lua_pop(L, 2); + // stack now contains: -1 => key; -2 => table + } + // stack now contains: -1 => table (when lua_next returns 0 it pops the key + // but does not push anything.) + // Pop table + lua_pop(L, 1); + // Stack is now the same as it was on entry to this function +} diff --git a/src/unix/read_cfg.c b/src/unix/read_cfg.c index b40dd48..602c750 100644 --- a/src/unix/read_cfg.c +++ b/src/unix/read_cfg.c @@ -20,8 +20,8 @@ char *find_config(){ int check_count = 2; int i; - sprintf(choices[0], "%s%c.config%cterminal-media-launcher%cconfig", home, sep, sep, sep); - sprintf(choices[1], "%s%c.terminal-media-launcher%cconfig", home, sep, sep); + sprintf(choices[0], "%s%c.config%cterminal-media-launcher%cconfig.lua", home, sep, sep, sep); + sprintf(choices[1], "%s%c.terminal-media-launcher%cconfig.lua", home, sep, sep); for(i = 0; i < check_count; i++){ strcpy(path, choices[i]); @@ -69,7 +69,7 @@ void mkconfig_wizard(char *path){ sprintf(path, "%s%c.config%cterminal-media-launcher%c", home, sep, sep, sep); mkdir(path, 0755); - sprintf(path, "%s%c.config%cterminal-media-launcher%cconfig", home, sep, sep, sep); + sprintf(path, "%s%c.config%cterminal-media-launcher%cconfig.lua", home, sep, sep, sep); //open file for writing, make sure non-NULL fp = fopen(path, "w"); @@ -78,56 +78,66 @@ void mkconfig_wizard(char *path){ exit(1); } - //write to file - fprintf(fp, "# This file was auto-generated by terminal-media-launcher. See docs/terminal-media-launcher-config.md or terminal-media-launcher-config(5) for documentation\n" - "# The default launcher is set to \"xdg-open\" which will open files based on the relevant default application set through xdg\n\n" - "# Recursively add files from %s%cMusic%c to Music group\n" - "addGroup Music\n" - "setLauncher Music xdg-open\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.aac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.aiff Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.alac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.au Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.flac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.m4a Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.mp3 Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.ogg Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.pcm Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.wav Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.wma Music\n\n", home, sep, sep); - fprintf(fp, "# Recursively add files from %s%cPictures%c to Pictures group\n" - "addGroup Pictures\n" - "setLauncher Pictures xdg-open\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epi Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps2 Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps3 Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epsf Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epsi Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.ept Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.gif Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.gfa Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.giff Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.jpeg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.jpg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.png Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.svg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.svgz Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.tif Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.tiff Pictures\n\n", home, sep, sep); - fprintf(fp, "# Recursively add files from %s%cVideos%c to Videos group\n" - "addGroup Videos\n" - "setLauncher Videos xdg-open\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.asf Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.avi Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.flv Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mk3d Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mkv Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mov Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mp4 Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.qt Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.webm Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.wmv Videos\n", home, sep, sep); + fprintf(fp, + "-- This file was auto-generated by terminal-media-launcher. See docs/terminal-media-launcher-config.md or terminal-media-launcher-config(5) for documentation\n" + "-- The default launcher is set to \"xdg-open\" which will open files based on the relevant default application set through xdg\n\n" + "local lfs = require \"lfs\"\n" + "\n" + "local function addGroup(name, launcher, flags)\n" + " assert(type(name) == \"string\")\n" + "\n" + " -- create Groups table if needed\n" + " if Groups == nil then\n" + " Groups = {}\n" + " end\n" + "\n" + " local new_group = {}\n" + " new_group.name = name\n" + " new_group.Entries = {}\n" + " if launcher ~= nil then\n" + " new_group.Launcher = launcher\n" + " end\n" + " if flags ~= nil then\n" + " new_group.Flags = flags\n" + " end\n" + "\n" + " table.insert(Groups, new_group)\n" + " return new_group\n" + "end\n" + "\n" + "local function addEntries(parentGroup, startDir, filePattern, recursive)\n" + " -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default)\n" + " assert(type(parentGroup) == \"table\")\n" + " assert(type(parentGroup.Entries) == \"table\")\n" + " assert(type(startDir) == \"string\")\n" + " assert(type(filePattern) == \"string\")\n" + "\n" + " for file in lfs.dir(startDir) do\n" + " local fullFilePath = startDir .. \"/\" .. file\n" + " if file ~= \".\" and file ~= \"..\" then\n" + " -- descend into subdirectory if recursive is set to true\n" + " if lfs.attributes(fullFilePath).mode == \"directory\" and recursive == true then\n" + " addEntries(parentGroup, fullFilePath, filePattern, recursive)\n" + " elseif lfs.attributes(fullFilePath).mode == \"file\" then\n" + " if string.match(file, filePattern) then\n" + " table.insert(parentGroup.Entries, {\n" + " name = file,\n" + " path = fullFilePath\n" + " })\n" + " end\n" + " end\n" + " end\n" + " end\n" + "end\n" + "\n" + "local music = addGroup(\"Music\", \"xdg-open\")\n" + "addEntries(music, os.getenv(\"HOME\") .. \"/Music\", \".*\", true)\n" + "\n" + "local pictures = addGroup(\"Pictures\", \"xdg-open\")\n" + "addEntries(pictures, os.getenv(\"HOME\") .. \"/Pictures\", \".*\", true)\n" + "\n" + "local videos = addGroup(\"Videos\", \"xdg-open\")\n" + "addEntries(videos, os.getenv(\"HOME\") .. \"/Videos\", \".*\", true)\n"); fclose(fp); printf("done\nIt is highly recommended to further tweak the configuration file! [press any key to continue]"); @@ -137,97 +147,3 @@ void mkconfig_wizard(char *path){ return; } - -//TODO augment to involve recurs -//TODO could use some cleanup... -void handle_fname(char *path, char *group, bool recurs, bool force, char *name, int ln){ - ENTRY *new; - char *search; //pointer for traversing path - char full_path_cpy[BUF_LEN]; - char relative_path_cpy[BUF_LEN]; - char arg_cpy[BUF_LEN]; - char auto_name[BUF_LEN]; - int plen = strlen(path); - char *dirname; - char *local_arg; //for use in addR - DIR *dp; - struct dirent *fname; - int i; - char *error_p; //helper for complex error messages - - assert(path != NULL && group != NULL); - - if(path[0] == '\0' || group[0] == '\0'){ - error_mes(ln, "Too few arguments for \"add\""); - return; - } - - //address potential quotes - strcpy(full_path_cpy, strip_quotes(path)); - - //don't check that the path arg is valid when forced - if(force) addme(full_path_cpy, group, force, name); - - //file is not recognized, perhaps it has a wildcard? - //TODO finish rewriting a more robust wildcard thingy - else if(access(full_path_cpy, F_OK) == -1){ - i = search_ch(full_path_cpy, '*'); - if(i > -1){ - //look for a directory - while(full_path_cpy[i] != sep && (i >= 0)){ - i--; - } - dirname = full_path_cpy; - strcpy(arg_cpy, full_path_cpy); - dirname[i+1] = '\0'; - dp = opendir(dirname); - - //the directory is real - if(dp != NULL){ - while((fname = readdir(dp))){ - relative_path_cpy[0] = '\0'; - strcat(relative_path_cpy, dirname); - strcat(relative_path_cpy, fname->d_name); - - //check if path is a file (and not a directory/symlink/etc.) and regex matches - if(fname->d_type == DT_REG && !(wild_cmp(&arg_cpy[i+1], fname->d_name))) addme(relative_path_cpy, group, force, name); - - //if the recursive option was specified and the path is a directory, run handle_fname on this directory, but for security reasons, do not consider directories that start with a '.' - else if(recurs && fname->d_type == DT_DIR && fname->d_name[0] != '.'){ - i = search_last_ch(arg_cpy, sep); - local_arg = &arg_cpy[i+1]; - strcat(relative_path_cpy, &sep); - strcat(relative_path_cpy, local_arg); - handle_fname(relative_path_cpy, group, 1, 0, NULL, ln); - } - - } - - closedir(dp); - } - - //directory is not real, report error to the user - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "\"%s\" bad path", dirname); - error_mes(ln, error_p); - free(error_p); - //printf("Error: \"%s\" bad path\n", dirname); - } - } - - //path is not real, report error to the user - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "\"%s\" bad path", full_path_cpy); - error_mes(ln, error_p); - free(error_p); - } - } - - //file name is okay - //FIXME does not take into account whether the argument is a file (could be a directory, symlink, etc.) - else addme(full_path_cpy, group, force, name); - - return; -} diff --git a/src/windows/draw.c b/src/windows/draw.c index 3b06b9f..cbc565f 100644 --- a/src/windows/draw.c +++ b/src/windows/draw.c @@ -6,10 +6,10 @@ #include "../include/group.h" void launch(){ + ENTRY **e = get_gentries(g[g_hover]); char *program = get_gprog(g[g_hover]); char *flags = get_gflags(g[g_hover]); char *path = get_epath(e[e_hover[g_hover]]); - bool quotes = get_gquotes(g[g_hover]); char file[BUF_LEN]; char params[BUF_LEN]; @@ -24,9 +24,7 @@ void launch(){ } else{ - if(quotes) strcat(file, "\""); strcat(file, program); - if(quotes) strcat(file, "\""); params[0] = '\0'; if(flags[0] != '\0'){ diff --git a/src/windows/read_cfg.c b/src/windows/read_cfg.c index f973759..79e56b7 100644 --- a/src/windows/read_cfg.c +++ b/src/windows/read_cfg.c @@ -20,7 +20,7 @@ char *find_config(){ char choices[check_count][BUF_LEN]; int i; - sprintf(choices[0], "%s%cterminal-media-launcher%cconfig", appdata, sep, sep); + sprintf(choices[0], "%s%cterminal-media-launcher%cconfig.lua", appdata, sep, sep); for(i = 0; i < check_count; i++){ strcpy(path, choices[i]); @@ -72,7 +72,7 @@ void mkconfig_wizard(char *path){ sprintf(path, "%s%cterminal-media-launcher%c", appdata, sep, sep); mkdir(path); - sprintf(path, "%s%cterminal-media-launcher%cconfig", appdata, sep, sep); + sprintf(path, "%s%cterminal-media-launcher%cconfig.lua", appdata, sep, sep); //open file for writing, make sure non-NULL fp = fopen(path, "w"); @@ -81,53 +81,66 @@ void mkconfig_wizard(char *path){ exit(1); } - fprintf(fp, "# This file was auto-generated by terminal-media-launcher. See docs\\terminal-media-launcher-config.md or terminal-media-launcher-config(5) for documentation\n" - "# By default, no launcher is specified for any group. When no launcher is specified on the Windows build of terminal-media-launcher, media files will be opened with their default application.\n" - "# It is generally recommended to specify a launcher for groups containing media files using the \"setLauncher\" command\n\n" - "# Recursively add files from %s%cMusic%c to Music group\n" - "addGroup Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.aac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.aiff Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.alac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.au Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.flac Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.m4a Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.mp3 Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.ogg Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.pcm Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.wav Music\n", home, sep, sep); - fprintf(fp, "addR %s%cMusic%c*.wma Music\n\n", home, sep, sep); - fprintf(fp, "# Recursively add files from %s%cPictures%c to Pictures group\n" - "addGroup Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epi Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps2 Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.eps3 Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epsf Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.epsi Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.ept Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.gif Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.gfa Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.giff Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.jpeg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.jpg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.png Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.svg Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.svgz Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.tif Pictures\n", home, sep, sep); - fprintf(fp, "addR %s%cPictures%c*.tiff Pictures\n\n", home, sep, sep); - fprintf(fp, "# Recursively add files from %s%cVideos%c to Videos group\n" - "addGroup Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.asf Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.avi Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.flv Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mk3d Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mkv Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mov Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.mp4 Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.qt Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.webm Videos\n", home, sep, sep); - fprintf(fp, "addR %s%cVideos%c*.wmv Videos\n", home, sep, sep); + fprintf(fp, + "-- This file was auto-generated by terminal-media-launcher. See docs\\terminal-media-launcher-config.md or terminal-media-launcher-config(5) for documentation\n" + "-- By default, no launcher is specified for any group. When no launcher is specified on the Windows build of terminal-media-launcher, media files will be opened with their default application.\n\n" + "local lfs = require \"lfs\"\n" + "\n" + "local function addGroup(name, launcher, flags)\n" + " assert(type(name) == \"string\")\n" + "\n" + " -- create Groups table if needed\n" + " if Groups == nil then\n" + " Groups = {}\n" + " end\n" + "\n" + " local new_group = {}\n" + " new_group.name = name\n" + " new_group.Entries = {}\n" + " if launcher ~= nil then\n" + " new_group.Launcher = launcher\n" + " end\n" + " if flags ~= nil then\n" + " new_group.Flags = flags\n" + " end\n" + "\n" + " table.insert(Groups, new_group)\n" + " return new_group\n" + "end\n" + "\n" + "local function addEntries(parentGroup, startDir, filePattern, recursive)\n" + " -- recursive arg is a boolean for whether or not to descend into subdirectories (false by default)\n" + " assert(type(parentGroup) == \"table\")\n" + " assert(type(parentGroup.Entries) == \"table\")\n" + " assert(type(startDir) == \"string\")\n" + " assert(type(filePattern) == \"string\")\n" + "\n" + " for file in lfs.dir(startDir) do\n" + " local fullFilePath = startDir .. \"\\\\\" .. file\n" + " if file ~= \".\" and file ~= \"..\" then\n" + " -- descend into subdirectory if recursive is set to true\n" + " if lfs.attributes(fullFilePath).mode == \"directory\" and recursive == true then\n" + " addEntries(parentGroup, fullFilePath, filePattern, recursive)\n" + " elseif lfs.attributes(fullFilePath).mode == \"file\" then\n" + " if string.match(file, filePattern) then\n" + " table.insert(parentGroup.Entries, {\n" + " name = file,\n" + " path = fullFilePath\n" + " })\n" + " end\n" + " end\n" + " end\n" + " end\n" + "end\n" + "\n" + "local music = addGroup(\"Music\")\n" + "addEntries(music, os.getenv(\"USERPROFILE\") .. \"\\\\Music\", \".*\", true)\n" + "\n" + "local pictures = addGroup(\"Pictures\")\n" + "addEntries(pictures, os.getenv(\"USERPROFILE\") .. \"\\\\Pictures\", \".*\", true)\n" + "\n" + "local videos = addGroup(\"Videos\")\n" + "addEntries(videos, os.getenv(\"USERPROFILE\") .. \"\\\\Videos\", \".*\", true)\n"); fclose(fp); printf("done\nIt is highly recommended to further tweak the configuration file! [press any key to continue]"); @@ -137,96 +150,3 @@ void mkconfig_wizard(char *path){ return; } - -//TODO augment to involve recurs -//TODO could use some cleanup... -void handle_fname(char *path, char *group, bool recurs, bool force, char *name, int ln){ - ENTRY *new; - char *search; //pointer for traversing path - char full_path_cpy[BUF_LEN]; - char relative_path_cpy[BUF_LEN]; - char arg_cpy[BUF_LEN]; - char auto_name[BUF_LEN]; - int plen = strlen(path); - char *dirname; - char *local_arg; //for use in addR - DIR *dp; - struct dirent *fname; - int i; - char *error_p; //helper for complex error messages - - assert(path != NULL && group != NULL); - - if(path[0] == '\0' || group[0] == '\0'){ - error_mes(ln, "Too few arguments for \"add\""); - return; - } - - //address potential quotes - strcpy(full_path_cpy, strip_quotes(path)); - - //don't check that the path arg is valid when forced - if(force) addme(full_path_cpy, group, force, name); - - //file is not recognized, perhaps it has a wildcard? - //TODO finish rewriting a more robust wildcard thingy - else if(access(full_path_cpy, F_OK) == -1){ - i = search_ch(full_path_cpy, '*'); - if(i > -1){ - //look for a directory - while(full_path_cpy[i] != sep && (i >= 0)){ - i--; - } - dirname = full_path_cpy; - strcpy(arg_cpy, full_path_cpy); - dirname[i+1] = '\0'; - dp = opendir(dirname); - - //the directory is real - if(dp != NULL){ - while(fname = readdir(dp)){ - relative_path_cpy[0] = '\0'; - strcat(relative_path_cpy, dirname); - strcat(relative_path_cpy, fname->d_name); - - //Windows cannot tell file types (TODO), so just add relatively indiscriminantly - if(!(wild_cmp(&arg_cpy[i+1], fname->d_name))) addme(relative_path_cpy, group, force, name); - - //if the recursive option was specified, run handle_fname on this directory, but for security reasons, do not consider directories that start with a '.' - else if(recurs && fname->d_name[0] != '.'){ - i = search_last_ch(arg_cpy, sep); - local_arg = &arg_cpy[i+1]; - strcat(relative_path_cpy, &sep); - strcat(relative_path_cpy, local_arg); - handle_fname(relative_path_cpy, group, 1, 0, NULL, ln); - } - } - - closedir(dp); - } - - //directory is not real, report error to the user - else if(!recurs){ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "\"%s\" bad path", dirname); - error_mes(ln, error_p); - free(error_p); - //printf("Error: \"%s\" bad path\n", dirname); - } - } - - //path is not real, report error to the user - else{ - error_p = malloc(sizeof(char) * 1024); - sprintf(error_p, "\"%s\" bad path", full_path_cpy); - error_mes(ln, error_p); - free(error_p); - } - } - - //file name is okay - //FIXME does not take into account whether the argument is a file (could be a directory, symlink, etc.) - else addme(full_path_cpy, group, force, name); - - return; -} |