Mod storage, network_discord_id_from_local_index and other small changes (#200)

* Mod storage & network_discord_id_from_local_index

Introducing mod storage, a key/value system for saving things like high scores or settings or anything like that to AppData\Roaming\sm64ex-coop\sav\mod-name.sav (Windows example)
You can currently save only strings meaning if you want to save a number you'd need to do
mod_storage_save("score", tostring(score))
and then
score = tonumber(mod_storage_load("score"))
To handle types like that.
network_discord_id_from_local_index() returns the discord ID of a player by local index.
Some misc changes I made were removing the unsed ini.h include in save_file.c and making the autogen converters output LF line endings instead of CRLF on windows.

* Prevent getting id if using direct connect

* Update djui_panel_player.c

Why does this bother me?

Co-authored-by: djoslin0 <djoslin0@users.noreply.github.com>
This commit is contained in:
Agent X 2022-09-26 22:28:26 -04:00 committed by GitHub
parent 8e9b2c270c
commit 4b84a87c6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 2281 additions and 42 deletions

View file

@ -234,7 +234,7 @@ def build_files(processed_files):
def build_to_c(built_files):
txt = ''
with open(get_path(in_filename), 'r') as f:
with open(get_path(in_filename), 'r', newline='\n') as f:
txt = f.read()
txt += '\n' + built_files
@ -327,7 +327,7 @@ def def_constant(processed_constant):
def build_to_def(processed_files):
s = '-- AUTOGENERATED FOR CODE EDITORS --\n\n'
with open(get_path(in_filename), 'r') as f:
with open(get_path(in_filename), 'r', newline='\n') as f:
s += f.read()
s += '\n'
@ -347,15 +347,15 @@ def main():
built_c = build_to_c(built_files)
with open(get_path(out_filename), 'w') as out:
with open(get_path(out_filename), 'w', newline='\n') as out:
out.write(built_c)
doc = doc_files(processed_files)
with open(get_path(out_filename_docs), 'w') as out:
with open(get_path(out_filename_docs), 'w', newline='\n') as out:
out.write(doc)
defs = build_to_def(processed_files)
with open(get_path(out_filename_defs), 'w') as out:
with open(get_path(out_filename_defs), 'w', newline='\n') as out:
out.write(defs)
global totalConstants

View file

@ -52,6 +52,7 @@ in_files = [
"src/game/object_list_processor.h",
"src/game/behavior_actions.h",
"src/game/mario_misc.h",
"src/pc/mods/mod_storage.h"
"src/pc/utils/misc.h",
"src/game/level_update.h"
]
@ -820,7 +821,7 @@ def doc_files(processed_files):
buffer = buffer.replace('$[FUNCTION_NAV_HERE', function_nav)
with open(get_path(out_filename_docs % page_name), 'w') as out:
with open(get_path(out_filename_docs % page_name), 'w', newline='\n') as out:
out.write(buffer)
############################################################################
@ -869,7 +870,7 @@ def def_files(processed_files):
for def_pointer in def_pointers:
s += '--- @class %s\n' % def_pointer
with open(get_path(out_filename_defs), 'w') as out:
with open(get_path(out_filename_defs), 'w', newline='\n') as out:
out.write(s)
############################################################################
@ -888,7 +889,7 @@ def main():
.replace("$[BINDS]", built_binds) \
.replace("$[INCLUDES]", built_includes)
with open(filename, 'w') as out:
with open(filename, 'w', newline='\n') as out:
out.write(gen)
print('REJECTS:\n%s' % rejects)

View file

@ -424,7 +424,7 @@ def doc_structs(structs):
continue
s += doc_struct(struct) + '\n'
with open(get_path(out_filename_docs), 'w') as out:
with open(get_path(out_filename_docs), 'w', newline='\n') as out:
out.write(s)
############################################################################
@ -472,7 +472,7 @@ def def_structs(structs):
for def_pointer in def_pointers:
s += '--- @class %s\n' % def_pointer
with open(get_path(out_filename_defs), 'w') as out:
with open(get_path(out_filename_defs), 'w', newline='\n') as out:
out.write(s)
############################################################################
@ -494,11 +494,11 @@ def build_files():
built_include = build_includes()
out_c_filename = get_path(out_filename_c)
with open(out_c_filename, 'w') as out:
with open(out_c_filename, 'w', newline='\n') as out:
out.write(c_template.replace("$[BODY]", built_body).replace('$[INCLUDES]', built_include))
out_h_filename = get_path(out_filename_h)
with open(out_h_filename, 'w') as out:
with open(out_h_filename, 'w', newline='\n') as out:
out.write(h_template.replace("$[BODY]", built_enum))
doc_structs(parsed)

View file

@ -5211,6 +5211,19 @@ function vec3f_set_dist_and_angle(from, to, dist, pitch, yaw)
-- ...
end
--- @param key string
--- @return string
function mod_storage_load(key)
-- ...
end
--- @param key string
--- @param value string
--- @return boolean
function mod_storage_save(key, value)
-- ...
end
--- @return nil
function update_all_mario_stars()
-- ...
@ -5276,6 +5289,12 @@ function network_player_set_description(np, description, r, g, b, a)
-- ...
end
--- @param localIndex integer
--- @return string
function network_discord_id_from_local_index(localIndex)
-- ...
end
--- @param localIndex integer
--- @return string
function network_get_player_text_color_string(localIndex)

View file

@ -7095,25 +7095,10 @@
<br />
---
# functions from misc.h
<br />
## [update_all_mario_stars](#update_all_mario_stars)
### Lua Example
`update_all_mario_stars()`
### Parameters
- None
### Returns
- None
### C Prototype
`void update_all_mario_stars(void);`
[:arrow_up_small:](#)
@ -7301,6 +7286,26 @@
<br />
## [network_discord_id_from_local_index](#network_discord_id_from_local_index)
### Lua Example
`local stringValue = network_discord_id_from_local_index(localIndex)`
### Parameters
| Field | Type |
| ----- | ---- |
| localIndex | `integer` |
### Returns
- `string`
### C Prototype
`char* network_discord_id_from_local_index(u8 localIndex);`
[:arrow_up_small:](#)
<br />
## [network_get_player_text_color_string](#network_get_player_text_color_string)
### Lua Example

View file

@ -1012,8 +1012,6 @@
<br />
- misc.h
- [update_all_mario_stars](functions-3.md#update_all_mario_stars)
<br />
@ -1030,6 +1028,7 @@
<br />
- network_utils.h
- [network_discord_id_from_local_index](functions-3.md#network_discord_id_from_local_index)
- [network_get_player_text_color_string](functions-3.md#network_get_player_text_color_string)
- [network_global_index_from_local](functions-3.md#network_global_index_from_local)
- [network_is_moderator](functions-3.md#network_is_moderator)

View file

@ -11,7 +11,6 @@
#include "course_table.h"
#include "rumble_init.h"
#include "macros.h"
#include "pc/ini.h"
#include "pc/network/network.h"
#include "pc/lua/utils/smlua_level_utils.h"
#include "pc/utils/misc.h"

1223
src/pc/configini.c Normal file

File diff suppressed because it is too large Load diff

110
src/pc/configini.h Normal file
View file

@ -0,0 +1,110 @@
/*
libconfigini - an ini formatted configuration parser library
Copyright (C) 2013-present Taner YILMAZ <taner44@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of copyright holders nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CONFIGINI_H_
#define CONFIGINI_H_
#include <stdio.h>
#include <stdbool.h>
typedef struct Config Config;
#define CONFIG_SECTION_FLAT NULL /* config is flat data (has no section) */
/**
* \brief Return types
*/
typedef enum
{
CONFIG_OK, /* ok (no error) */
CONFIG_ERR_FILE, /* file io error (file not exists, cannot open file, ...) */
CONFIG_ERR_NO_SECTION, /* section does not exist */
CONFIG_ERR_NO_KEY, /* key does not exist */
CONFIG_ERR_MEMALLOC, /* memory allocation failed */
CONFIG_ERR_INVALID_PARAM, /* invalid parametrs (as NULL) */
CONFIG_ERR_INVALID_VALUE, /* value of key is invalid (inconsistent data, empty data) */
CONFIG_ERR_PARSING, /* parsing error of data (does not fit to config format) */
} ConfigRet;
#ifdef __cplusplus
extern "C" {
#endif
Config* ConfigNew (void);
void ConfigFree (Config *cfg);
const char *ConfigRetToString (ConfigRet ret);
ConfigRet ConfigRead (FILE *fp, Config **cfg);
ConfigRet ConfigReadFile (const char *filename, Config **cfg);
ConfigRet ConfigPrint (const Config *cfg, FILE *stream);
ConfigRet ConfigPrintToFile (const Config *cfg, char *filename);
ConfigRet ConfigPrintSettings (const Config *cfg, FILE *stream);
int ConfigGetSectionCount (const Config *cfg);
int ConfigGetKeyCount (const Config *cfg, const char *sect);
ConfigRet ConfigSetCommentCharset(Config *cfg, const char *comment_ch);
ConfigRet ConfigSetKeyValSepChar (Config *cfg, char ch);
ConfigRet ConfigSetBoolString (Config *cfg, const char *true_str, const char *false_str);
ConfigRet ConfigReadString (const Config *cfg, const char *sect, const char *key, char * val, int size, const char * dfl_val);
ConfigRet ConfigReadInt (const Config *cfg, const char *sect, const char *key, int * val, int dfl_val);
ConfigRet ConfigReadUnsignedInt (const Config *cfg, const char *sect, const char *key, unsigned int *val, unsigned int dfl_val);
ConfigRet ConfigReadFloat (const Config *cfg, const char *sect, const char *key, float * val, float dfl_val);
ConfigRet ConfigReadDouble (const Config *cfg, const char *sect, const char *key, double * val, double dfl_val);
ConfigRet ConfigReadBool (const Config *cfg, const char *sect, const char *key, bool * val, bool dfl_val);
ConfigRet ConfigAddString (Config *cfg, const char *sect, const char *key, const char *val);
ConfigRet ConfigAddInt (Config *cfg, const char *sect, const char *key, int val);
ConfigRet ConfigAddUnsignedInt (Config *cfg, const char *sect, const char *key, unsigned int val);
ConfigRet ConfigAddFloat (Config *cfg, const char *sect, const char *key, float val);
ConfigRet ConfigAddDouble (Config *cfg, const char *sect, const char *key, double val);
ConfigRet ConfigAddBool (Config *cfg, const char *sect, const char *key, bool val);
bool ConfigHasSection (const Config *cfg, const char *sect);
ConfigRet ConfigRemoveSection (Config *cfg, const char *sect);
ConfigRet ConfigRemoveKey (Config *cfg, const char *sect, const char *key);
#ifdef __cplusplus
}
#endif
#endif /* CONFIGINI_H_ */

View file

@ -113,7 +113,7 @@ void djui_panel_player_edit_palette_destroy(struct DjuiBase* caller) {
}
static void djui_panel_player_edit_palette_create(struct DjuiBase* caller) {
char* sPartStrings[PLAYER_PART_MAX] = { "Pants", "Shirt", "Gloves", "Shoes", "Hair", "Skin" };
char* sPartStrings[PLAYER_PART_MAX] = { "Overalls", "Shirt", "Gloves", "Shoes", "Hair", "Skin" };
f32 bodyHeight = 32 * 5 + 64 * 1 + 16 * 5;

View file

@ -33,6 +33,7 @@
#include "src/game/mario_misc.h"
#include "src/pc/utils/misc.h"
#include "src/game/level_update.h"
#include "src/pc/mods/mod_storage.h"
////////////////////////
@ -11718,15 +11719,6 @@ int smlua_func_vec3s_to_vec3f(lua_State* L) {
}
*/
////////////
// misc.h //
////////////
int smlua_func_update_all_mario_stars(UNUSED lua_State* L) {
if(!smlua_functions_valid_param_count(L, 0)) { return 0; }
update_all_mario_stars();
return 1;
}
@ -11867,6 +11859,17 @@ int smlua_func_network_player_set_description(lua_State* L) {
// network_utils.h //
/////////////////////
int smlua_func_network_discord_id_from_local_index(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 1)) { return 0; }
u8 localIndex = smlua_to_integer(L, 1);
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1 for function 'network_discord_id_from_local_index'"); return 0; }
lua_pushstring(L, network_discord_id_from_local_index(localIndex));
return 1;
}
int smlua_func_network_get_player_text_color_string(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 1)) { return 0; }
@ -19202,8 +19205,6 @@ void smlua_bind_functions_autogen(void) {
//smlua_bind_function(L, "vec3s_sum", smlua_func_vec3s_sum); <--- UNIMPLEMENTED
//smlua_bind_function(L, "vec3s_to_vec3f", smlua_func_vec3s_to_vec3f); <--- UNIMPLEMENTED
// misc.h
smlua_bind_function(L, "update_all_mario_stars", smlua_func_update_all_mario_stars);
// network_player.h
smlua_bind_function(L, "get_network_player_from_area", smlua_func_get_network_player_from_area);
@ -19216,6 +19217,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "network_player_set_description", smlua_func_network_player_set_description);
// network_utils.h
smlua_bind_function(L, "network_discord_id_from_local_index", smlua_func_network_discord_id_from_local_index);
smlua_bind_function(L, "network_get_player_text_color_string", smlua_func_network_get_player_text_color_string);
smlua_bind_function(L, "network_global_index_from_local", smlua_func_network_global_index_from_local);
smlua_bind_function(L, "network_is_moderator", smlua_func_network_is_moderator);

165
src/pc/mods/mod_storage.c Normal file
View file

@ -0,0 +1,165 @@
#include "mod_storage.h"
#include <stdio.h>
#include "pc/platform.h"
#include "pc/configini.h" // for writing
#include "pc/ini.h" // for parsing
#include "pc/lua/smlua.h"
#include "pc/mods/mods_utils.h"
#include "pc/debuglog.h"
void strdelete(char string[], char substr[]) {
// i is used to loop through the string
u16 i = 0;
// store the lengths of the string and substr
u16 string_length = strlen(string);
u16 substr_length = strlen(substr);
// loop through starting at the first index
while (i < string_length) {
// if we find the substr at the current index, delete it
if (strstr(&string[i], substr) == &string[i]) {
// determine the string's new length after removing the substr occurrence
string_length -= substr_length;
// shift forward the remaining characters in the string after the substr
// occurrence by the length of substr, effectively removing it!
for (u16 j = i; j < string_length; j++) {
string[j] = string[j + substr_length];
}
} else {
i++;
}
}
string[i] = '\0';
}
bool char_valid(char* buffer) {
if (buffer[0] == '\0') { return false; }
while (*buffer != '\0') {
if ((*buffer >= 'a' && *buffer <= 'z') || (*buffer >= 'A' && *buffer <= 'Z') || (*buffer >= '0' && *buffer <= '9') || *buffer == '_' || *buffer == '.') {
buffer++;
continue;
}
return false;
}
return true;
}
u32 key_count(char* filename) {
FILE *file;
file = fopen(filename, "r");
if (file == NULL) { return 0; }
u32 lines = 1;
char c;
do {
c = fgetc(file);
if (c == '\n') { lines++; }
} while (c != EOF);
fclose(file);
return lines - 4;
}
char *mod_storage_get_filename(char* dest) {
const char *path = sys_user_path(); // get base sm64ex-coop appdata dir
snprintf(dest, SYS_MAX_PATH - 1, "%s/sav/%s", path, gLuaActiveMod->relativePath); // append sav folder
strdelete(dest, ".lua"); // delete ".lua" from sav name
strcat(dest, SAVE_EXTENSION); // append SAVE_EXTENSION
normalize_path(dest); // fix any out of place slashes
}
bool mod_storage_save(const char *key, const char *value) {
if (strlen(key) > MAX_KEY_VALUE_LENGTH || strlen(value) > MAX_KEY_VALUE_LENGTH) {
LOG_LUA_LINE("Too long of a key and or value for mod_storage_save()");
return false;
}
if (!char_valid((char *)key) || !char_valid((char *)value)) {
LOG_LUA_LINE("Invalid key and or value passed to mod_storage_save()");
return false;
}
FILE *file;
Config *cfg = NULL;
char *filename;
filename = (char *)malloc((SYS_MAX_PATH - 1) * sizeof(char));
mod_storage_get_filename(filename);
// ensure savPath exists
char savPath[SYS_MAX_PATH] = { 0 };
if (snprintf(savPath, SYS_MAX_PATH - 1, "%s", fs_get_write_path(SAVE_DIRECTORY)) < 0) {
LOG_ERROR("Failed to concat sav path");
free(filename);
return false;
}
if (!fs_sys_dir_exists(savPath)) { fs_sys_mkdir(savPath); }
bool exists = path_exists(filename);
file = fopen(filename, exists ? "r+" : "w");
cfg = ConfigNew();
if (exists) {
if (ConfigReadFile(filename, &cfg) != CONFIG_OK) {
ConfigFree(cfg);
fclose(file);
free(filename);
return false;
}
if (key_count(filename) > MAX_KEYS) {
LOG_LUA_LINE("Tried to save more than MAX_KEYS with mod_storage_save()");
ConfigFree(cfg);
fclose(file);
free(filename);
return false;
}
}
ConfigRemoveKey(cfg, "storage", key);
ConfigAddString(cfg, "storage", key, value);
ConfigPrint(cfg, file);
ConfigFree(cfg);
fclose(file);
free(filename);
return true;
}
const char *mod_storage_load(const char *key) {
if (strlen(key) > MAX_KEY_VALUE_LENGTH) {
LOG_LUA_LINE("Too long of a key for mod_storage_load()");
return NULL;
}
if (!char_valid((char *)key)) {
LOG_LUA_LINE("Invalid key passed to mod_storage_save()");
return NULL;
}
char *filename;
filename = (char *)malloc((SYS_MAX_PATH - 1) * sizeof(char));
mod_storage_get_filename(filename);
static char value[MAX_KEY_VALUE_LENGTH];
ini_t *storage;
if (!path_exists(filename)) {
free(filename);
return NULL;
}
storage = ini_load(filename);
if (storage == NULL) {
ini_free(storage);
free(filename);
return NULL;
}
snprintf(value, MAX_KEY_VALUE_LENGTH, "%s", ini_get(storage, "storage", key));
ini_free(storage);
free(filename);
if (strstr(value, "(null)") != NULL) { return NULL; }
return value;
}

14
src/pc/mods/mod_storage.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef MOD_STORAGE_H
#define MOD_STORAGE_H
#include "mod.h"
#define MAX_KEYS 255
#define MAX_KEY_VALUE_LENGTH 64
#define SAVE_DIRECTORY "sav"
#define SAVE_EXTENSION ".sav"
bool mod_storage_save(const char *key, const char *value);
const char *mod_storage_load(const char *key);
#endif

View file

@ -1,5 +1,6 @@
#include <stdio.h>
#include "network_utils.h"
#include "discord/discord.h"
#include "game/mario_misc.h"
u8 network_global_index_from_local(u8 localIndex) {
@ -22,6 +23,11 @@ u8 network_local_index_from_global(u8 globalIndex) {
return globalIndex + ((globalIndex < gNetworkPlayerLocal->globalIndex) ? 1 : 0);
}
char* network_discord_id_from_local_index(u8 localIndex) {
if (gNetworkSystem == &gNetworkSystemDiscord) { return gNetworkSystem->get_id_str(localIndex); }
return NULL;
}
bool network_is_server(void) {
return gNetworkType == NT_SERVER;
}

View file

@ -7,6 +7,8 @@
u8 network_global_index_from_local(u8 localIndex);
u8 network_local_index_from_global(u8 globalIndex);
char* network_discord_id_from_local_index(u8 localIndex);
bool network_is_server(void);
bool network_is_moderator(void);

694
src/pc/queue.h Normal file
View file

@ -0,0 +1,694 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* $FreeBSD: head/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
#include <sys/cdefs.h>
/*
* This file defines four types of data structures: singly-linked lists,
* singly-linked tail queues, lists and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A singly-linked tail queue is headed by a pair of pointers, one to the
* head of the list and the other to the tail of the list. The elements are
* singly linked for minimum space and pointer manipulation overhead at the
* expense of O(n) removal for arbitrary elements. New elements can be added
* to the list after an existing element, at the head of the list, or at the
* end of the list. Elements being removed from the head of the tail queue
* should use the explicit macro for this purpose for optimum efficiency.
* A singly-linked tail queue may only be traversed in the forward direction.
* Singly-linked tail queues are ideal for applications with large datasets
* and few or no removals or for implementing a FIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may be traversed in either direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*
*
* SLIST LIST STAILQ TAILQ
* _HEAD + + + +
* _HEAD_INITIALIZER + + + +
* _ENTRY + + + +
* _INIT + + + +
* _EMPTY + + + +
* _FIRST + + + +
* _NEXT + + + +
* _PREV - + - +
* _LAST - - + +
* _FOREACH + + + +
* _FOREACH_FROM + + + +
* _FOREACH_SAFE + + + +
* _FOREACH_FROM_SAFE + + + +
* _FOREACH_REVERSE - - - +
* _FOREACH_REVERSE_FROM - - - +
* _FOREACH_REVERSE_SAFE - - - +
* _FOREACH_REVERSE_FROM_SAFE - - - +
* _INSERT_HEAD + + + +
* _INSERT_BEFORE - + - +
* _INSERT_AFTER + + + +
* _INSERT_TAIL - - + +
* _CONCAT - - + +
* _REMOVE_AFTER + - + -
* _REMOVE_HEAD + - + -
* _REMOVE + + + +
* _SWAP + + + +
*
*/
#ifdef QUEUE_MACRO_DEBUG
/* Store the last 2 places the queue element or head was altered */
struct qm_trace {
unsigned long lastline;
unsigned long prevline;
const char *lastfile;
const char *prevfile;
};
#define TRACEBUF struct qm_trace trace;
#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } ,
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
#define QMD_TRACE_HEAD(head) do { \
(head)->trace.prevline = (head)->trace.lastline; \
(head)->trace.prevfile = (head)->trace.lastfile; \
(head)->trace.lastline = __LINE__; \
(head)->trace.lastfile = __FILE__; \
} while (0)
#define QMD_TRACE_ELEM(elem) do { \
(elem)->trace.prevline = (elem)->trace.lastline; \
(elem)->trace.prevfile = (elem)->trace.lastfile; \
(elem)->trace.lastline = __LINE__; \
(elem)->trace.lastfile = __FILE__; \
} while (0)
#else
#define QMD_TRACE_ELEM(elem)
#define QMD_TRACE_HEAD(head)
#define QMD_SAVELINK(name, link)
#define TRACEBUF
#define TRACEBUF_INITIALIZER
#define TRASHIT(x)
#endif /* QUEUE_MACRO_DEBUG */
/*
* Singly-linked List declarations.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_FOREACH(var, head, field) \
for ((var) = SLIST_FIRST((head)); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != NULL; \
(varp) = &SLIST_NEXT((var), field))
#define SLIST_INIT(head) do { \
SLIST_FIRST((head)) = NULL; \
} while (0)
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
SLIST_NEXT((slistelm), field) = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
SLIST_FIRST((head)) = (elm); \
} while (0)
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
if (SLIST_FIRST((head)) == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} \
else { \
struct type *curelm = SLIST_FIRST((head)); \
while (SLIST_NEXT(curelm, field) != (elm)) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_REMOVE_AFTER(curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
SLIST_NEXT(elm, field) = \
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
} while (0)
#define SLIST_SWAP(head1, head2, type) do { \
struct type *swap_first = SLIST_FIRST(head1); \
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
SLIST_FIRST(head2) = swap_first; \
} while (0)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
struct type **stqh_last;/* addr of last next element */ \
}
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (0)
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \
for((var) = STAILQ_FIRST((head)); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((tqelm), field) = (elm); \
} while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? NULL : \
__containerof((head)->stqh_last, struct type, field.stqe_next))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} \
else { \
struct type *curelm = STAILQ_FIRST((head)); \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
STAILQ_REMOVE_AFTER(head, curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
if ((STAILQ_NEXT(elm, field) = \
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_SWAP(head1, head2, type) do { \
struct type *swap_first = STAILQ_FIRST(head1); \
struct type **swap_last = (head1)->stqh_last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_FIRST(head2) = swap_first; \
(head2)->stqh_last = swap_last; \
if (STAILQ_EMPTY(head1)) \
(head1)->stqh_last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
(head2)->stqh_last = &STAILQ_FIRST(head2); \
} while (0)
/*
* List declarations.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_LIST_CHECK_HEAD(head, field) do { \
if (LIST_FIRST((head)) != NULL && \
LIST_FIRST((head))->field.le_prev != \
&LIST_FIRST((head))) \
panic("Bad list head %p first->prev != head", (head)); \
} while (0)
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
if (LIST_NEXT((elm), field) != NULL && \
LIST_NEXT((elm), field)->field.le_prev != \
&((elm)->field.le_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
#define QMD_LIST_CHECK_PREV(elm, field) do { \
if (*(elm)->field.le_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_LIST_CHECK_HEAD(head, field)
#define QMD_LIST_CHECK_NEXT(elm, field)
#define QMD_LIST_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
QMD_LIST_CHECK_NEXT(listelm, field); \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
QMD_LIST_CHECK_PREV(listelm, field); \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
QMD_LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_PREV(elm, head, type, field) \
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
__containerof((elm)->field.le_prev, struct type, field.le_next))
#define LIST_REMOVE(elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
QMD_LIST_CHECK_NEXT(elm, field); \
QMD_LIST_CHECK_PREV(elm, field); \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
} while (0)
#define LIST_SWAP(head1, head2, type, field) do { \
struct type *swap_tmp = LIST_FIRST((head1)); \
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
LIST_FIRST((head2)) = swap_tmp; \
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
} while (0)
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
/*
* Tail queue functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
if (!TAILQ_EMPTY(head) && \
TAILQ_FIRST((head))->field.tqe_prev != \
&TAILQ_FIRST((head))) \
panic("Bad tailq head %p first->prev != head", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
if (*(head)->tqh_last != NULL) \
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
} while (0)
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
if (TAILQ_NEXT((elm), field) != NULL && \
TAILQ_NEXT((elm), field)->field.tqe_prev != \
&((elm)->field.tqe_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
if (*(elm)->field.tqe_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_TAILQ_CHECK_HEAD(head, field)
#define QMD_TAILQ_CHECK_TAIL(head, headname)
#define QMD_TAILQ_CHECK_NEXT(elm, field)
#define QMD_TAILQ_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
QMD_TRACE_HEAD(head1); \
QMD_TRACE_HEAD(head2); \
} \
} while (0)
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST((head), headname); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
QMD_TAILQ_CHECK_NEXT(listelm, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
} \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&listelm->field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
QMD_TAILQ_CHECK_PREV(listelm, field); \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&listelm->field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
QMD_TAILQ_CHECK_HEAD(head, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
QMD_TAILQ_CHECK_TAIL(head, field); \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
QMD_TAILQ_CHECK_NEXT(elm, field); \
QMD_TAILQ_CHECK_PREV(elm, field); \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
QMD_TRACE_HEAD(head); \
} \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_SWAP(head1, head2, type, field) do { \
struct type *swap_first = (head1)->tqh_first; \
struct type **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)
#endif /* !_SYS_QUEUE_H_ */