15 Commits
Author SHA1 Message Date
Ikey Doherty e3360b7ddc Bump v3
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:16:46 +00:00
Ikey Doherty 1700f2616c Ensure we report that lsi-steam.desktop borrows translations
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:15:50 +00:00
Ikey Doherty 6f8e905569 Update README for --disable-replace-steam, remove excess in text
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:09:50 +00:00
Ikey Doherty 7c0ff5031e data: Incorporate actions from primary steam.desktop
Steam no longer writes a custom .desktop in into the local user directory,
so if the user is using a desktop that supports pinning a .desktop file,
it will be this one.

Thus, we enable the same actions, but going through the proxy of LSI.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:56:58 +00:00
Ikey Doherty dd5237e648 shim: Launch vanilla /usr/bin/steam when replacement is disabled
When we're not built for distro integration, we'll now call out to
/usr/bin/steam with the LSI modifications in place, so that we don't
enter into an infinite recursion.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:54:20 +00:00
Ikey Doherty 2282296045 Add an option to disable replacement of the Steam binary
LSI was initially created for Solus inclusion, and the best approach
determined was to replace /usr/bin/steam with our own stand in shim,
so that it was fully integrated.

Doing so enables one to run steam from anywhere and it would correctly
appear in the path, with no issues. However, this made LSI suitable
only for distribution integration, such as the very tight integration
seen in Solus.

With this change, the binary name will change to lsi-steam, and an
additional .desktop file to launch the shim will be installed. The
user must then use the LSI specific .desktop file to launch Steam,
at which point they'll be going through the LSI shim system and
reaping the benefits, without having to modify any packages.

More changes are yet to come, which will configure lsi-steam to call
the "real" steam in exec rather than the masked libdir variant.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:45:02 +00:00
Ikey Doherty ba48264207 update_format: Exclude nica from modification
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:28:07 +00:00
Ikey Doherty bddc0f29b1 Hook libnica into autotools and use this version now
TODO: Make coverage of libnica more intelligent so that we can drop
the extra conditional.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:16:20 +00:00
Ikey Doherty 196a039746 Include libnica as a git submodule
This ensures we only need to maintain the library in one place, greatly
reducing the complexity of integration and updating of the library.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:13:33 +00:00
Ikey Doherty 4ff98e225a Remove our local forked copy of libnica
This will pave the way for embedding libnica as a submodule and building
against the static library, removing any requirements for having the shared
library present.

As it seems nica fits better currently as a utility library, that's not
such a bad thing.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:12:11 +00:00
Ikey Doherty e49a7873fe Preload libxcb now with latest Steam runtime
It explicitly checks for xcb_poll_for_reply64, which must be preloaded before
libx11 to ensure it's properly resolved.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-10-12 04:40:31 +01:00
Ikey Doherty 6659905102 Merge pull request #4 from philipzae/patch-1
correct link to solus website
2016-07-25 22:03:38 +01:00
Yousuf 'Jay' Philips 16702d0d8f correct link to solus website 2016-07-25 23:44:14 +04:00
Ikey Doherty 00d8831720 nica: Sync with upstream commit a8b1f1ee423f386d1ff676d4173ebd83a64656de
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-06-15 13:01:23 +01:00
Ikey Doherty 451ffe7680 nica: Sync with upstream
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-06-11 18:47:54 +01:00
14 changed files with 206 additions and 1260 deletions
+1
View File
@@ -15,4 +15,5 @@ m4/*.m4
*.la
.libs
/steam
/lsi-steam
/lsi-settings
+3
View File
@@ -0,0 +1,3 @@
[submodule "src/libnica"]
path = src/libnica
url = git@github.com:ikeydoherty/libnica.git
+40 -23
View File
@@ -1,7 +1,8 @@
EXTRA_DIST = \
LICENSE.LGPL2.1 \
README.rst \
data/lsi-settings.desktop
data/lsi-settings.desktop \
data/lsi-steam.desktop
AM_CFLAGS = \
-fstack-protector -Wall -pedantic \
@@ -12,19 +13,20 @@ AM_CFLAGS = \
-Wall -W -D_FORTIFY_SOURCE=2 -std=c99
AM_CPPFLAGS = \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/shim \
-I $(top_srcdir)/src/nica \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/shim \
-I $(top_srcdir)/src/libnica/src/include \
-DSYSTEMCONFDIR=\"$(sysconfdir)\" \
-DVENDORDIR=\"$(datadir)/defaults/linux-steam-integration\"
SUBDIRS = src/libnica
noinst_LTLIBRARIES = \
libnica.la \
liblsi.la
bin_PROGRAMS = \
steam
bin_PROGRAMS =
desktopdir = $(datadir)/applications
desktop_DATA =
if HAVE_FRONTEND
bin_PROGRAMS += lsi-settings
@@ -43,40 +45,55 @@ lsi_settings_LDADD = \
$(GTK_LIBS)
desktopdir = $(datadir)/applications
desktop_DATA = \
desktop_DATA += \
data/lsi-settings.desktop
endif
# Convenience library to prevent external linking with currently unstable ABI
libnica_la_SOURCES = \
src/nica/hashmap.h \
src/nica/hashmap.c \
src/nica/inifile.h \
src/nica/inifile.c \
src/nica/util.h
libnica_la_CFLAGS = \
$(AM_CFLAGS)
# Convenience library for shared LSI functions
liblsi_la_SOURCES = \
src/shim/lsi.h \
src/shim/lsi.c
liblsi_la_CFLAGS = \
$(AM_CFLAGS)
$(AM_CFLAGS) \
-I$(top_srcdir)/src/libnica/src/include/nica
liblsi_la_LIBADD = \
libnica.la
src/libnica/libnica.la
# Replace the main steam binary with our own steam file
if REPLACE_STEAM
bin_PROGRAMS += steam
steam_SOURCES = \
src/shim/shim.c
steam_LDADD = \
libnica.la \
src/libnica/libnica.la \
liblsi.la
steam_CFLAGS = \
$(AM_CFLAGS)
else
# Don't replace steam - use a non conflicting filename
bin_PROGRAMS += lsi-steam
lsi_steam_SOURCES = \
src/shim/shim.c
lsi_steam_LDADD = \
src/libnica/libnica.la \
liblsi.la
lsi_steam_CFLAGS = \
$(AM_CFLAGS)
# Install custom .desktop file for steam
desktop_DATA += \
data/lsi-steam.desktop
endif
+21 -11
View File
@@ -8,7 +8,7 @@ This project, and by extension Solus, is not officially endorsed by, or
affiliated with, Steam*, or its parent company, Valve*.
Linux Steam Integration (LSI) is a `Solus Project <https://github.com/solus-project/linux-steam-integration/issues/2>`_
Linux Steam Integration (LSI) is a `Solus Project <https://solus-project.com/>`_
.. image:: https://build.solus-project.com/logo.png
:align: center
@@ -22,12 +22,24 @@ To correctly integrate LSI, your Steam package will require modification.
LSI must provide the /usr/bin/steam binary, so your Steam package must
move the main launcher to a shadow location.
For users who do not have access to LSI in their distribution, because it has
not been integrated yet, please configure with ``--disable-replace-steam`` to
preserve your existing Steam integrity. Then use the "LSI Steam" shortcut in
your menu to launch Steam via LSI.
**Configuring LSI build**
There are a number of configure options you should be aware of when integrating
LSI correctly. The prominent ones are explained here.
``--disable-replace-steam``
This will disable building a ``steam`` binary, and instead build an
``lsi-steam`` binary, and install ``lsi-steam.desktop``. Use this
option if you are not a distribution integrator, and wish to use
LSI yourself.
``--with-real-steam-binary=$NAME``
@@ -40,6 +52,10 @@ LSI correctly. The prominent ones are explained here.
/usr/lib/steam/steam
This option is only applicable for distribution integrators, when
Steam replacement is enabled.
``--with-preload-libs=$LIBS``
A colon separated list of libraries that are required to launch Steam
@@ -124,16 +140,6 @@ Common issues
License
-------
``src/nica``::
Partial import of libnica, Copyright © Intel Corporation.
Used within the shim component to ensure it is leak free
and as lightweight as possible, due to needing to exec the
main Steam* launcher.
libnica is available under the terms of the `LGPL-2.1` license.
``src/shim src/frontend``::
Copyright © 2016 Ikey Doherty
@@ -141,6 +147,10 @@ License
linux-steam-integration is available under the terms of the `LGPL-2.1`
``data/lsi-steam.desktop``::
This file borrows translations from the official ``steam.desktop`` launcher.
These are copyright of Valve*.
See `LICENSE.LGPL2.1 <LICENSE.LGPL2.1>`_ for more details
+19 -3
View File
@@ -1,4 +1,4 @@
AC_INIT([linux-steam-integration], 0.2, [ikey@solus-project.com], [linux-steam-integration], [https://github.com/solus-project/linux-steam-integration/issues])
AC_INIT([linux-steam-integration], 0.3, [ikey@solus-project.com], [linux-steam-integration], [https://github.com/solus-project/linux-steam-integration/issues])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects])
AC_PROG_CC
@@ -11,7 +11,12 @@ AM_SILENT_RULES([yes])
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_FILES([Makefile])
# Turn on the nica submodule support
AM_CONDITIONAL([NICA_SUBMODULE], true)
AM_CONDITIONAL([COVERAGE], false)
AC_CONFIG_FILES([Makefile
src/libnica/Makefile])
# Check if we're using the new C++ ABI, which will require LD_PRELOAD magic
AC_LANG_PUSH([C++])
@@ -60,7 +65,7 @@ AC_DEFINE_UNQUOTED(STEAM_BINARY, "$STEAMBIN",
# The LD_PRELOAD list required to integrate gracefully with the official Steam Runtime
AC_ARG_WITH([preload-libs], AS_HELP_STRING([--with-preload-libs],
[the preload libs to use with Steams own runtime]), [preloadlibs="${withval}"],
[preloadlibs="/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6"])
[preloadlibs="/usr/\$LIB/libxcb.so.1:/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6"])
AC_DEFINE_UNQUOTED([LSI_PRELOAD_LIBS], "$preloadlibs",
[Libraries to preload with Steams runtime])
@@ -78,6 +83,16 @@ if test "x$enable_frontend" = "xyes" ; then
fi
AM_CONDITIONAL([HAVE_FRONTEND], [test x$have_frontend = "xyes"])
# Whether LSI should stand in for steam binary
replace_steam="yes"
AC_ARG_ENABLE(replace-steam, AS_HELP_STRING([--disable-replace-steam], [disable Steam binary replacement]))
if test "x$enable_replace_steam" = "xno"; then
replace_steam="no"
fi
if test "x$replace_steam" = "xyes" ; then
AC_DEFINE([REPLACE_STEAM], [1], [Steam binary will be replaced])
fi
AM_CONDITIONAL([REPLACE_STEAM], [test x$replace_steam = "xyes"])
AC_OUTPUT
@@ -93,6 +108,7 @@ AC_MSG_RESULT([
datarootdir: ${datarootdir}
New C++ ABI: ${use_new_abi}
Replace Steam: ${replace_steam}
Real Steam binary: ${STEAMBIN}
GTK+ Frontend: ${have_frontend}
preload-libs: ${preloadlibs}
+107
View File
@@ -0,0 +1,107 @@
[Desktop Entry]
Name=LSI Steam
Comment=Launch Steam via LSI
Exec=/usr/bin/lsi-steam %U
Icon=steam
Terminal=false
Type=Application
Categories=Network;FileTransfer;Game;
MimeType=x-scheme-handler/steam;
Actions=Store;Community;Library;Servers;Screenshots;News;Settings;BigPicture;Friends;
[Desktop Action Store]
Name=Store
Name[de]=Shop
Name[es]=Tienda
Name[fr]=Magasin
Name[it]=Negozio
Name[pt]=Loja
Name[ru]=Магазин
Name[zh_CN]=商店
Name[zh_TW]=商店
Exec=lsi-steam steam://store
[Desktop Action Community]
Name=Community
Name[es]=Comunidad
Name[fr]=Communauté
Name[it]=Comunità
Name[pt]=Comunidade
Name[ru]=Сообщество
Name[zh_CN]=社区
Name[zh_TW]=社群
Exec=lsi-steam steam://url/SteamIDControlPage
[Desktop Action Library]
Name=Library
Name[de]=Bibliothek
Name[es]=Biblioteca
Name[fr]=Bibliothèque
Name[it]=Libreria
Name[pt]=Biblioteca
Name[ru]=Библиотека
Name[zh_CN]=
Name[zh_TW]=遊戲庫
Exec=lsi-steam steam://open/games
[Desktop Action Servers]
Name=Servers
Name[de]=Server
Name[es]=Servidores
Name[fr]=Serveurs
Name[it]=Server
Name[pt]=Servidores
Name[ru]=Серверы
Name[zh_CN]=服务器
Name[zh_TW]=伺服器
Exec=lsi-steam steam://open/servers
[Desktop Action Screenshots]
Name=Screenshots
Name[es]=Capturas
Name[fr]=Captures d'écran
Name[it]=Screenshot
Name[ru]=Скриншоты
Name[zh_CN]=截图
Name[zh_TW]=螢幕擷圖
Exec=lsi-steam steam://open/screenshots
[Desktop Action News]
Name=News
Name[de]=Neuigkeiten
Name[es]=Noticias
Name[fr]=Actualités
Name[it]=Notizie
Name[pt]=Notícias
Name[ru]=Новости
Name[zh_CN]=新闻
Name[zh_TW]=新聞
Exec=lsi-steam steam://open/news
[Desktop Action Settings]
Name=Settings
Name[de]=Einstellungen
Name[es]=Parámetros
Name[fr]=Paramètres
Name[it]=Impostazioni
Name[pt]=Configurações
Name[ru]=Настройки
Name[zh_CN]=设置
Name[zh_TW]=設定
Exec=lsi-steam steam://open/settings
[Desktop Action BigPicture]
Name=Big Picture
Exec=lsi-steam steam://open/bigpicture
[Desktop Action Friends]
Name=Friends
Name[de]=Freunde
Name[es]=Amigos
Name[fr]=Amis
Name[it]=Amici
Name[pt]=Amigos
Name[ru]=Друзья
Name[zh_CN]=好友
Name[zh_TW]=好友
Exec=lsi-steam steam://open/friends
Submodule
+1
Submodule src/libnica added at e71a442667
-472
View File
@@ -1,472 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#define INITIAL_SIZE 61
/* When we're "full", 70% */
#define FULL_FACTOR 0.7
/* Multiple to increase bucket size by */
#define INCREASE_FACTOR 4
/**
* An bucket/chain within the hashmap
*/
typedef struct NcHashmapEntry {
void *hash; /**<The key for this item */
void *value; /**<Value for this item */
struct NcHashmapEntry *next; /**<Next item in the chain */
bool occ; /**<Whether this bucket is occupied */
} NcHashmapEntry;
/**
* A NcHashmap
*/
struct NcHashmap {
int size; /**<Current size of the hashmap */
int next_size; /**<Next size at which we need to resize */
int n_buckets; /**<Current number of buckets */
NcHashmapEntry *buckets; /**<Stores our bucket chains */
nc_hash_create_func hash; /**<Hash generation function */
nc_hash_compare_func compare; /**<Key comparison function */
nc_hash_free_func key_free; /**<Cleanup function for keys */
nc_hash_free_func value_free; /**<Cleanup function for values */
};
/**
* Iteration object
*/
typedef struct _NcHashmapIter {
int bucket; /**<Current bucket position */
NcHashmap *map; /**<Associated NcHashmap */
void *item; /**<Current item in this iteration */
} _NcHashmapIter;
static void nc_hashmap_update_next_size(NcHashmap *self);
static bool nc_hashmap_resize(NcHashmap *self);
static inline bool nc_hashmap_maybe_resize(NcHashmap *self)
{
if (!self) {
return false;
}
if (self->size >= self->next_size) {
return true;
}
return false;
}
static NcHashmap *nc_hashmap_new_internal(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
NcHashmap *map = NULL;
NcHashmapEntry *buckets = NULL;
map = calloc(1, sizeof(NcHashmap));
if (!map) {
return NULL;
}
buckets = calloc(INITIAL_SIZE, sizeof(NcHashmapEntry));
if (!buckets) {
free(map);
return NULL;
}
map->buckets = buckets;
map->n_buckets = INITIAL_SIZE;
map->hash = create ? create : nc_simple_hash;
map->compare = compare ? compare : nc_simple_compare;
map->key_free = key_free;
map->value_free = value_free;
map->size = 0;
nc_hashmap_update_next_size(map);
return map;
}
NcHashmap *nc_hashmap_new(nc_hash_create_func create, nc_hash_compare_func compare)
{
return nc_hashmap_new_internal(create, compare, NULL, NULL);
}
NcHashmap *nc_hashmap_new_full(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
return nc_hashmap_new_internal(create, compare, key_free, value_free);
}
static inline unsigned nc_hashmap_get_hash(NcHashmap *self, const void *key)
{
unsigned hash = self->hash(key);
return hash;
}
static bool nc_hashmap_insert_bucket(NcHashmap *self, NcHashmapEntry *buckets, int n_buckets,
unsigned hash, const void *key, void *value)
{
NcHashmapEntry *row = &(buckets[hash % n_buckets]);
NcHashmapEntry *head = NULL;
NcHashmapEntry *parent = head = row;
bool can_replace = false;
NcHashmapEntry *tomb = NULL;
int ret = 1;
while (row) {
if (!row->occ) {
tomb = row;
}
parent = row;
if (row->occ && row->hash == key) {
if (self->compare(row->hash, key)) {
can_replace = true;
break;
}
}
row = row->next;
}
if (can_replace) {
/* Replace existing allocations. */
if (self->value_free) {
self->value_free(row->value);
}
if (self->key_free) {
self->key_free(row->hash);
}
ret = 0;
} else if (tomb) {
row = tomb;
}
if (!row) {
row = calloc(1, sizeof(NcHashmapEntry));
if (!row) {
return -1;
}
}
row->hash = (void *)key;
row->value = value;
row->occ = true;
if (parent != row && parent) {
parent->next = row;
}
return ret;
}
bool nc_hashmap_put(NcHashmap *self, const void *key, void *value)
{
if (!self) {
return false;
}
int inc;
if (nc_hashmap_maybe_resize(self)) {
if (!nc_hashmap_resize(self)) {
return false;
}
}
unsigned hash = nc_hashmap_get_hash(self, key);
inc = nc_hashmap_insert_bucket(self, self->buckets, self->n_buckets, hash, key, value);
if (inc > 0) {
self->size += inc;
return true;
} else {
return false;
}
}
static NcHashmapEntry *nc_hashmap_get_entry(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
unsigned hash = nc_hashmap_get_hash(self, key);
NcHashmapEntry *row = &(self->buckets[hash % self->n_buckets]);
while (row) {
if (self->compare(row->hash, key)) {
return row;
}
row = row->next;
}
return NULL;
}
void *nc_hashmap_get(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (row) {
return row->value;
}
return NULL;
}
static bool nc_hashmap_remove_internal(NcHashmap *self, const void *key, bool remove)
{
if (!self) {
return false;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (!row) {
return false;
}
if (remove) {
if (self->key_free) {
self->key_free(row->hash);
}
if (self->value_free) {
self->value_free(row->value);
}
}
self->size -= 1;
row->hash = NULL;
row->value = NULL;
row->occ = false;
return true;
}
bool nc_hashmap_steal(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, false);
}
bool nc_hashmap_remove(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, true);
}
bool nc_hashmap_contains(NcHashmap *self, const void *key)
{
return (nc_hashmap_get(self, key)) != NULL;
}
static inline void nc_hashmap_free_bucket(NcHashmap *self, NcHashmapEntry *bucket, bool nuke)
{
if (!self) {
return;
}
NcHashmapEntry *tmp = bucket;
NcHashmapEntry *bk = bucket;
NcHashmapEntry *root = bucket;
while (tmp) {
bk = NULL;
if (tmp->next) {
bk = tmp->next;
}
if (nuke && tmp->occ) {
if (self->key_free) {
self->key_free(tmp->hash);
}
if (self->value_free) {
self->value_free(tmp->value);
}
}
if (tmp != root) {
free(tmp);
}
tmp = bk;
}
}
void nc_hashmap_free(NcHashmap *self)
{
if (!self) {
return;
}
for (int i = 0; i < self->n_buckets; i++) {
NcHashmapEntry *row = &(self->buckets[i]);
nc_hashmap_free_bucket(self, row, true);
}
if (self->buckets) {
free(self->buckets);
}
free(self);
}
static void nc_hashmap_update_next_size(NcHashmap *self)
{
if (!self) {
return;
}
self->next_size = (int)(self->n_buckets * FULL_FACTOR);
}
int nc_hashmap_size(NcHashmap *self)
{
if (!self) {
return -1;
}
return self->size;
}
static bool nc_hashmap_resize(NcHashmap *self)
{
if (!self || !self->buckets) {
return false;
}
NcHashmapEntry *old_buckets = self->buckets;
NcHashmapEntry *new_buckets = NULL;
NcHashmapEntry *entry = NULL;
int incr;
int old_size, new_size;
int items = 0;
new_size = old_size = self->n_buckets;
new_size *= INCREASE_FACTOR;
new_buckets = calloc(new_size, sizeof(NcHashmapEntry));
if (!new_buckets) {
return false;
}
for (int i = 0; i < old_size; i++) {
entry = &(old_buckets[i]);
while (entry) {
if (entry->occ) {
unsigned hash = nc_hashmap_get_hash(self, entry->hash);
if ((incr = nc_hashmap_insert_bucket(self,
new_buckets,
new_size,
hash,
entry->hash,
entry->value)) > 0) {
items += incr;
} else {
/* Likely a memory issue */
goto failure;
}
}
entry = entry->next;
}
}
/* Successfully resized - do this separately because we need to
* gaurantee old data is preserved */
for (int i = 0; i < old_size; i++) {
nc_hashmap_free_bucket(self, &(old_buckets[i]), false);
}
free(old_buckets);
self->n_buckets = new_size;
self->size = items;
self->buckets = new_buckets;
nc_hashmap_update_next_size(self);
return true;
failure:
for (int i = 0; i < new_size; i++) {
nc_hashmap_free_bucket(self, &(new_buckets[i]), true);
}
free(new_buckets);
return false;
}
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *citer)
{
_NcHashmapIter *iter = NULL;
if (!map || !citer) {
return;
}
iter = (_NcHashmapIter *)citer;
_NcHashmapIter it = {
.bucket = -1, .map = map, .item = NULL,
};
*iter = it;
}
bool nc_hashmap_iter_next(NcHashmapIter *citer, void **key, void **value)
{
_NcHashmapIter *iter = NULL;
NcHashmapEntry *item = NULL;
NcHashmap *map = NULL;
int n_buckets = 0;
if (!citer) {
return false;
}
iter = (_NcHashmapIter *)citer;
map = iter->map;
if (!map) {
return false;
}
n_buckets = map->n_buckets;
item = iter->item;
for (;;) {
if (iter->bucket >= n_buckets) {
if (item && !item->next) {
return false;
}
}
if (!item) {
iter->bucket++;
if (iter->bucket > n_buckets - 1) {
return false;
}
item = &(map->buckets[iter->bucket]);
}
if (item && item->occ) {
goto success;
}
item = item->next;
}
return false;
success:
iter->item = item->next;
if (key) {
*key = item->hash;
}
if (value) {
*value = item->value;
}
return true;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-239
View File
@@ -1,239 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This is a chained Hashmap implementation. It focuses on being clean
* and efficient, and is comparable (at -O2) to open addressing hashmaps
* up until around 105,000 elements, where open addressing will begin
* to come out in front. At 1 million elements, open addressing is a clear
* winner, however currently we only need a maximum of 70k elements in
* our implementation.
*
* Given the memory required to even begin to deal with 1 million elements,
* it becomes very questionable whether a hashmap should have even been
* used in the first place (mmap'd db?)
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include "util.h"
/* Convert between uint and void* */
#define NC_HASH_KEY(x) ((void *)((uintptr_t)(x)))
#define NC_HASH_VALUE(x) NC_HASH_KEY(x)
#define NC_UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x)))
#define NC_UNHASH_VALUE(x) NC_UNHASH_KEY(x)
typedef struct NcHashmap NcHashmap;
/**
* Iteration object
*/
typedef struct NcHashmapIter {
int n0;
void *n1;
void *n2;
} NcHashmapIter;
/**
* Hash comparison function definition
*
* @param l First value to compare
* @param r Second value to compare
*
* @return true if l and r both match, otherwise false
*/
typedef bool (*nc_hash_compare_func)(const void *l, const void *r);
/**
* Hash creation function definition
*
* @param key Key to generate a hash for
*
* @return an unsigned integer hash result
*/
typedef unsigned (*nc_hash_create_func)(const void *key);
/**
* Callback definition to free keys and values
*
* @param p Single-depth pointer to either a key or value that should be freed
*/
typedef void (*nc_hash_free_func)(void *p);
/**
* Default hash/comparison functions
*
* @note These are only used for comparison of *keys*, not values. Unless
* explicitly using string keys, you should most likely stick with the default
* nc_simple_hash and nc_simple_compare functions
*/
/* Default string hash */
static inline unsigned nc_string_hash(const void *key)
{
unsigned hash = 5381;
const signed char *c;
/* DJB's hash function */
for (c = key; *c != '\0'; c++) {
hash = (hash << 5) + hash + (unsigned)*c;
}
return hash;
}
/**
* Trivial pointer->uint hash
*/
static inline unsigned nc_simple_hash(const void *source)
{
return NC_UNHASH_KEY(source);
}
/**
* Comparison of string keys
*/
static inline bool nc_string_compare(const void *l, const void *r)
{
if (!l || !r) {
return false;
}
return (strcmp(l, r) == 0);
}
/**
* Trivial pointer comparison
*/
static inline bool nc_simple_compare(const void *l, const void *r)
{
return (l == r);
}
/**
* Create a new NcHashmap
*
* @param hash Hash creation function
* @param compare Key comparison function
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new(nc_hash_create_func hash, nc_hash_compare_func compare);
/**
* Create a new NcHashmap with cleanup functions
*
* @param hash Hash creation function
* @param compare Key comparison function
* @param key_free Function to free keys when removed/destroyed
* @param value_free Function to free values when removed/destroyed
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new_full(nc_hash_create_func hash, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free);
/**
* Store a key/value pair in the hashmap
*
* @note This will displace duplicate keys, and may free both the key
* and value if key_free and value_free are non null
*
* @param key Key to store in the hashmap
* @param value Value to be associated with the key
*
* @return true if the operation succeeded.
*/
bool nc_hashmap_put(NcHashmap *map, const void *key, void *value);
/**
* Get the value associated with the unique key
*
* @param key Unique key to obtain a value for
* @return The associated value if it exists, otherwise NULL
*/
void *nc_hashmap_get(NcHashmap *map, const void *key);
/**
* Determine if the key has an associated value in the NcHashmap
*
* @param key Unique key to check value for
* @return True if the key exists in the hashmap
*/
bool nc_hashmap_contains(NcHashmap *self, const void *key);
/**
* Free the given NcHashmap, and all keys/values if appropriate
*/
void nc_hashmap_free(NcHashmap *map);
/**
* Remove the value and key identified by key.
*
* @note If key_free or value_free are non-NULL, they will be invoked
* for both the key and value being removed
*
* @param key The unique key to remove
* @return true if the key/value pair were removed
*/
bool nc_hashmap_remove(NcHashmap *map, const void *key);
/**
* Remove the value and key identified by key without freeing them
*
* @return true if the key/value pair were stolen
*/
bool nc_hashmap_steal(NcHashmap *map, const void *key);
/**
* Return the current size of the hashmap
*
* @return size of the current hashmap (element count)
*/
int nc_hashmap_size(NcHashmap *map);
/**
* Initialise a NcHashmapIter for iteration purposes
*
* @note The iter *must* be re-inited to re-use, or if it becomes
* exhausted from a previous iteration run
*
* @param iter pointer to a NcHashmapIter
*/
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *iter);
/**
* Iterate every key/value pair in the hashmap
*
* @param iter A correctly initialised NcHashmapIter
* @param key Pointers to store the key in, may be NULL to skip
* @param value Pointer to store the value in, may be NULL to skip
*
* @return true if it's possible to iterate
*/
bool nc_hashmap_iter_next(NcHashmapIter *iter, void **key, void **value);
DEF_AUTOFREE(NcHashmap, nc_hashmap_free)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-341
View File
@@ -1,341 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "inifile.h"
/**
* Mapping of @NcIniError to static strings
*/
static const char *_errors[] = {[NC_INI_ERROR_FILE] = "",
[NC_INI_ERROR_EMPTY_KEY] = "Encountered empty key",
[NC_INI_ERROR_NOT_CLOSED] = "Expected closing \']\' for section",
[NC_INI_ERROR_NO_SECTION] =
"Encountered key=value mapping without a valid section",
[NC_INI_ERROR_INVALID_LINE] = "Expected key=value notation" };
const char *nc_ini_error(NcIniError error)
{
int j = abs(error);
if (j < NC_INI_ERROR_FILE || j >= NC_INI_ERROR_MAX) {
return "[Unknown Error]";
}
return _errors[j];
}
/**
* Strip leading whitespace from string (left)
*/
static char *lstrip(char *str, size_t len, size_t *out_len)
{
size_t skip_len = 0;
for (size_t i = 0; i < len; i++) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
}
break;
}
*out_len = len - skip_len;
if (skip_len > 0) {
char *c = strdup(str + skip_len);
free(str);
return c;
}
return str + skip_len;
}
/**
* Strip trailing whitespace from string (right)
*/
static char *rstrip(char *str, size_t len, size_t *out_len)
{
size_t skip_len = 0;
for (int i = len; i > 0; i--) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
} else if (str[i] == '\0') {
continue;
}
break;
}
*out_len = len - skip_len;
if (len == skip_len) {
return str;
}
str[(len - skip_len)] = '\0';
return str;
}
/**
* Chew both ends of a null terminated string of whitespace
* If the left side is whitespace padded, this will always result
* in a new allocation due to fast-forwarding the pointer. Keep
* this in mind.
*/
static char *string_chew_terminated(char *inp)
{
int skip_offset = 0;
int len = 0;
int end_len = 0;
bool count = true;
if (!inp) {
return NULL;
}
for (char *c = inp; *c; c++) {
if (count && (*c == ' ' || *c == '\t')) {
++skip_offset;
continue;
} else {
count = false;
}
++len;
}
for (int i = len; i > skip_offset; i--) {
if (inp[i] == ' ' || inp[i] == '\t') {
++end_len;
continue;
} else if (inp[i] == '\0') {
continue;
}
break;
}
if (end_len > 0) {
inp[(len - end_len)] = '\0';
}
if (skip_offset > 0) {
char *c = strdup(inp + skip_offset);
free(inp);
return c;
}
return inp;
}
/**
* Munch both ends of the string
*/
static char *string_strip(char *str, size_t len, size_t *out_len)
{
size_t mylen = len;
char *c = lstrip(str, len, &mylen);
c = rstrip(c, mylen, &mylen);
if (out_len) {
*out_len = mylen;
}
return c;
}
NcHashmap *nc_ini_file_parse(const char *path)
{
NcHashmap *ret = NULL;
int error_line = 0;
int r = 0;
r = nc_ini_file_parse_full(path, &ret, &error_line);
if (r != 0) {
if (abs(r) == NC_INI_ERROR_FILE) {
fprintf(stderr, "[inifile] %s: %s\n", strerror(errno), path);
} else {
fprintf(stderr,
"[inifile] %s [L%d]: %s\n",
nc_ini_error(r),
error_line,
path);
}
return NULL;
}
return ret;
}
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number)
{
autofree(FILE) *file = NULL;
char *buf = NULL;
ssize_t r = 0;
size_t sn = 0;
int line_count = 1;
char *current_section = NULL;
NcHashmap *root_map = NULL;
NcHashmap *section_map = NULL;
bool failed = false;
int err_ret = 0;
file = fopen(path, "r");
if (!file) {
return -(NC_INI_ERROR_FILE);
}
if (!out_map) {
fprintf(stderr, "nc_ini_file_parse_full(): NcHashmap pointer invalid\n");
return -1;
}
root_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
(nc_hash_free_func)nc_hashmap_free);
while ((r = getline(&buf, &sn, file)) != -1) {
char *ch = NULL;
size_t str_len = r;
/* Fix newline */
if (buf[r - 1] == '\n') {
buf[r - 1] = '\0';
--r;
}
str_len = r;
buf = string_strip(buf, r, &str_len);
/* Empty lines are fine */
if (streq(buf, "")) {
goto next;
}
if (buf[0] == '[') {
/* Validate section start */
if (buf[str_len - 1] != ']') {
/* Throw error */
err_ret = NC_INI_ERROR_NOT_CLOSED;
goto fail;
}
/* Grab the section name, and "close" last section */
buf[str_len - 1] = '\0';
if (current_section) {
free(current_section);
}
current_section = strdup(buf + 1);
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
/* Create a new section dynamically */
section_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
free);
nc_hashmap_put(root_map, strdup(current_section), section_map);
}
goto next;
} else if (buf[0] == '#' || buf[0] == ';') {
/* Skip comment */
goto next;
}
/* Look for key = value */
ch = strchr(buf, '=');
if (!ch) {
/* Throw error */
err_ret = NC_INI_ERROR_INVALID_LINE;
goto fail;
}
/* Can't have sectionless k->v */
if (!current_section) {
err_ret = NC_INI_ERROR_NO_SECTION;
goto fail;
}
int offset = ch - buf;
/* Grab the key->value from this assignment line */
char *value = strdup((buf + offset) + 1);
buf[offset] = '\0';
char *key = strdup(buf);
key = string_chew_terminated(key);
value = string_chew_terminated(value);
if (streq(key, "")) {
err_ret = NC_INI_ERROR_EMPTY_KEY;
free(key);
free(value);
goto fail;
}
/* Ensure a section mapping exists */
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
err_ret = 1;
fprintf(stderr,
"[inifile] Fatal! No section map for named "
"section: %s\n",
current_section);
abort();
}
/* Insert these guys into the map */
if (!nc_hashmap_put(section_map, key, value)) {
err_ret = 1;
fprintf(stderr, "[inifile] Fatal! Out of memory\n");
abort();
}
/* Progression + cleanup */
next:
if (buf) {
free(buf);
buf = NULL;
}
++line_count;
sn = 0;
continue;
/* Parsing error, bail */
fail:
failed = true;
if (error_line_number) {
*error_line_number = line_count;
}
break;
}
if (buf) {
free(buf);
buf = NULL;
}
if (current_section) {
free(current_section);
current_section = NULL;
}
if (failed) {
if (root_map) {
nc_hashmap_free(root_map);
}
return -(err_ret);
}
*out_map = root_map;
return 0;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-111
View File
@@ -1,111 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#pragma once
#include "hashmap.h"
#include "util.h"
/**
* The Nica INI Parser focuses on simplicity and ease of use, therefore it
* currently only supports a read-only parse operation.
*
* The parse functions are used to return a _root_ @NcHashmap which contains
* name to section mappings. Each section is in turn an @NcHashmap with string
* to string mappings.
*
* Consider the following example:
*
* [Person]
* alive = true
* ; This person is alive
*
* The returned @NcHashmap will contain the key "Person", which corresponds to
* another @NcHashmap. This @NcHashmap will contain the key "alive", which has
* the value "true". Therefore we can access this variable using the @NcHashmap
* API:
*
* char *alive = nc_hashmap_get(nc_hashmap_get(ini, "Person"), "alive");
*
* Conversely, we can use the API to determine whether a section or key is set:
*
* if (nc_hashmap_contains(ini, "Person"))
*
* As @NcHashmap returns NULL in nc_hashmap_get, you can happily pass NULL
* keys, allowing quick key fetches without checking if the section exists.
*
* In our INI file, lines beginning with ";" or "#" are considered as comments
* and are not processed. A line following the "key = value" notation is an
* _assignment_. "[Section]" is considered to be a section in the INI file, and
* all INI files must have at least one section.
*
* We do allow duplication section definitions, by following a merge policy. If
* a key is redefined then the original key->value mapping is freed and the new
* key->value mapping is inserted.
*
* The parser will return a correctly configured @NcHashmap, the only cleanup
* required is to call nc_hashmap_free on it. Alternatively, use the autofree
* helper, for a RAII approach:
*
* autofree(NcHashmap) *map = nc_ini_file_parse("myfile.ini");
*/
/**
* Errors that are reported when parsing an ini file
*/
typedef enum {
NC_INI_ERROR_MIN = 0,
NC_INI_ERROR_FILE, /**< File based error, check strerror(errno) */
NC_INI_ERROR_EMPTY_KEY, /**< Empty key in an assignment line */
NC_INI_ERROR_NOT_CLOSED, /**< Encountered section start that wasn't closed with a ']' */
NC_INI_ERROR_NO_SECTION, /**< Key assignment with no defined sections */
NC_INI_ERROR_INVALID_LINE, /**< Encountered an invalid line (syntax) */
NC_INI_ERROR_MAX
} NcIniError;
/**
* Convenience wrapper for nc_ini_file_parse_full, reports errors to stderrr
* and returns an NcHashmap if the parsing succeeded
*/
NcHashmap *nc_ini_file_parse(const char *path);
/**
* Parse an INI file into a hash of hashes.
*
* @param path Path on the filesystem to parse, must be in INI syntax.
* @param out_map Pointer to store the resulting root NcHashmap in
* @param error_line_number If not null, then the erronous line number is
* stored.
*
* @return 0 if the call was succesful, or a negative integer. See nc_ini_error
*/
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number);
/**
* Return a string representation for a given @NcIniError
*
* @param error Valid NcIniError code
* @return a static string owned by the implementation
*/
const char *nc_ini_error(NcIniError error);
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-53
View File
@@ -1,53 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEF_AUTOFREE(N, C) \
static inline void _autofree_func_##N(void *p) \
{ \
if (p && *(N **)p) { \
C(*(N **)p); \
(*(void **)p) = NULL; \
} \
}
#define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N
/**
* Dump any leaked file descriptors
*/
void nc_dump_file_descriptor_leaks(void);
#define streq(x, y) strcmp(x, y) == 0 ? true : false
DEF_AUTOFREE(char, free)
DEF_AUTOFREE(FILE, fclose)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+13 -6
View File
@@ -32,10 +32,17 @@ int main(int argc, char **argv)
bool is_x86_64;
const char *n_argv[argc + 2];
const char *exec_command = NULL;
/* Use the appropriate Steam depending on configuration */
const char *lsi_exec_bin = NULL;
int i = 1;
int8_t off = 0;
int (*vfunc)(const char *, char *const argv[]) = NULL;
#ifdef REPLACE_STEAM
lsi_exec_bin = STEAM_BINARY;
#else
lsi_exec_bin = "/usr/bin/steam";
#endif
/* Initialise config */
if (!lsi_config_load(&config)) {
lsi_config_load_defaults(&config);
@@ -43,8 +50,8 @@ int main(int argc, char **argv)
is_x86_64 = lsi_system_is_64bit();
if (!lsi_file_exists(STEAM_BINARY)) {
lsi_report_failure("Steam isn't currently installed at %s", STEAM_BINARY);
if (!lsi_file_exists(lsi_exec_bin)) {
lsi_report_failure("Steam isn't currently installed at %s", lsi_exec_bin);
return EXIT_FAILURE;
}
@@ -70,14 +77,14 @@ int main(int argc, char **argv)
if (config.force_32 && is_x86_64) {
exec_command = EMUL32BIN;
n_argv[0] = EMUL32BIN;
n_argv[1] = STEAM_BINARY;
n_argv[1] = lsi_exec_bin;
off = 1;
/* Use linux32 in the path */
vfunc = execvp;
} else {
/* Directly call STEAM_BINARY */
exec_command = STEAM_BINARY;
n_argv[0] = STEAM_BINARY;
/* Directly call lsi_exec_bin */
exec_command = lsi_exec_bin;
n_argv[0] = lsi_exec_bin;
/* Full path here due to shadow nature */
vfunc = execv;
}
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/bash
clang-format -i $(find . -name '*.[ch]')
clang-format -i $(find . -not -path '*/libnica/*' -name '*.[ch]')