xaizek / vifm (License: GPLv2+) (since 2018-12-07)
Vifm is a file manager with curses interface, which provides Vi[m]-like environment for managing objects within file systems, extended with some useful ideas from mutt.
<root> / src / int / desktop.c (20ad2a0379f31202e8bcde7ea79e7bf040389348) (3,954B) (mode 100644) [raw]
/* vifm
 * Copyright (C) 2011 xaizek.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "desktop.h"

#if defined(ENABLE_DESKTOP_FILES)

#include <dirent.h>

#include <stdio.h> /* snprintf() */
#include <string.h> /* strstr() strchr() strlen() strcpy() */

#include "../compat/dtype.h"
#include "../compat/fs_limits.h"
#include "../compat/os.h"
#include "../utils/path.h"
#include "../utils/str.h"
#include "../utils/utils.h"
#include "../filetype.h"

static const char EXEC_KEY[] = "Exec=";
static const char MIMETYPE_KEY[] = "MimeType=";
static const char NAME_KEY[] = "Name=";

static const char CAPTION_MACRO = 'c';
static const char FILE_MACROS[] = "Uuf";

static void parse_desktop_files_internal(const char path[],
		const char mime_type[], assoc_records_t *result);
static void process_file(const char *path, const char *file_mime_type,
		assoc_records_t *result);
static void expand_desktop(const char *str, char *buf);

assoc_records_t
parse_desktop_files(const char *path, const char *mime_type)
{
	assoc_records_t result = {};
	parse_desktop_files_internal(path, mime_type, &result);
	return result;
}

static void
parse_desktop_files_internal(const char path[], const char mime_type[],
		assoc_records_t *result)
{
	DIR *dir;
	struct dirent *dentry;
	const char *slash;

	if((dir = os_opendir(path)) == NULL)
	{
		return;
	}

	slash = ends_with_slash(path) ? "" : "/";

	while((dentry = os_readdir(dir)) != NULL)
	{
		char full_path[PATH_MAX + 1];

		if(is_builtin_dir(dentry->d_name))
		{
			continue;
		}

		snprintf(full_path, sizeof(full_path), "%s%s%s", path, slash,
				dentry->d_name);
		if(get_dirent_type(dentry, full_path) == DT_DIR)
		{
			parse_desktop_files(full_path, mime_type);
		}
		else
		{
			process_file(full_path, mime_type, result);
		}
	}

	os_closedir(dir);
}

static void
process_file(const char *path, const char *file_mime_type,
		assoc_records_t *result)
{
	FILE *f;
	char exec[1024] = "", mime_type[2048] = "", name[2048] = "";
	char buf[2048];

	if(!ends_with(path, ".desktop") || (f = os_fopen(path, "r")) == NULL)
	{
		return;
	}

	while(fgets(buf, sizeof(buf), f) != NULL)
	{
		chomp(buf);

		if(starts_with(buf, EXEC_KEY))
		{
			copy_str(exec, sizeof(exec), buf + (ARRAY_LEN(EXEC_KEY) - 1));
		}
		else if(starts_with(buf, MIMETYPE_KEY))
		{
			copy_str(mime_type, sizeof(mime_type),
					buf + (ARRAY_LEN(MIMETYPE_KEY) - 1));
		}
		else if(starts_with(buf, NAME_KEY))
		{
			copy_str(name, sizeof(name), buf + (ARRAY_LEN(NAME_KEY) - 1));
		}
	}

	fclose(f);

	if(strstr(mime_type, file_mime_type) == NULL || exec[0] == '\0')
	{
		return;
	}

	expand_desktop(exec, buf);
	ft_assoc_record_add(result, buf, name);
}

static void
expand_desktop(const char *str, char *buf)
{
	int substituted = 0;
	while(*str != '\0')
	{
		if(*str != '%')
		{
			*buf++ = *str++;
			continue;
		}

		str++;
		if(*str == CAPTION_MACRO)
		{
			strcpy(buf, "caption");
			buf += strlen(buf);
		}
		else if(char_is_one_of(FILE_MACROS, *str))
		{
			substituted = 1;
			strcpy(buf, "%f");
			buf += strlen(buf);
		}
		str++;
	}

	*buf = substituted ? '\0' : ' ';
	if(!substituted)
	{
		strcpy(buf + 1, "%f");
	}
}

#endif

/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
/* vim: set cinoptions+=t0 filetype=c : */
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/vifm

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/vifm

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master