src/core/parser/parse_param.c
08327019
 /*
420b6bb0
  * Generic Parameter Parser
  *
c32feee5
  * Copyright (C) 2001-2003 FhG Fokus
420b6bb0
  *
07ca8c05
  * This file is part of Kamailio, a free SIP server.
420b6bb0
  *
07ca8c05
  * Kamailio is free software; you can redistribute it and/or modify
420b6bb0
  * 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
  *
07ca8c05
  * Kamailio is distributed in the hope that it will be useful,
420b6bb0
  * 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.
  *
08327019
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
420b6bb0
  *
  */
 
1eb01c66
 /*! \file
  * \brief Parser :: Generic Parameter Parser
  *
  * \ingroup parser
  */
 
420b6bb0
 #include <string.h>
 #include "../str.h"
 #include "../ut.h"
 #include "../dprint.h"
 #include "../trim.h"
 #include "../mem/mem.h"
91804f9b
 #include "../mem/shm_mem.h"
420b6bb0
 #include "parse_param.h"
 
 
08327019
 static inline void parse_event_dialog_class(param_hooks_t *h, param_t *p)
2eb780d7
 {
 
08327019
 	if(!p->name.s) {
 		LM_ERR("empty value\n");
2eb780d7
 		return;
 	}
08327019
 	if(!h) {
 		LM_CRIT("BUG:  NULL param hook pointer\n");
2eb780d7
 		return;
 	}
 	switch(p->name.s[0]) {
08327019
 		case 'c':
 		case 'C':
 			if((p->name.len == 7)
 					&& (!strncasecmp(p->name.s + 1, "all-id", 6))) {
 				p->type = P_CALL_ID;
 				h->event_dialog.call_id = p;
 			}
 			break;
 
 		case 'f':
 		case 'F':
 			if((p->name.len == 8)
 					&& (!strncasecmp(p->name.s + 1, "rom-tag", 7))) {
 				p->type = P_FROM_TAG;
 				h->event_dialog.from_tag = p;
 			}
 			break;
 
 		case 't':
 		case 'T':
 			if((p->name.len == 6)
 					&& (!strncasecmp(p->name.s + 1, "o-tag", 5))) {
 				p->type = P_TO_TAG;
 				h->event_dialog.to_tag = p;
 			}
 			break;
 
 		case 'i':
 		case 'I':
 			if((p->name.len == 27)
 					&& (!strncasecmp(p->name.s + 1,
 							"nclude-session-description", 26))) {
 				p->type = P_ISD;
 				h->event_dialog.include_session_description = p;
 			}
 			break;
 
 		case 's':
 		case 'S':
 			if((p->name.len == 3) && (!strncasecmp(p->name.s + 1, "la", 2))) {
 				p->type = P_SLA;
 				h->event_dialog.sla = p;
 			}
 			break;
 
 		case 'm':
 		case 'M':
 			if((p->name.len == 2) && (!strncasecmp(p->name.s + 1, "a", 1))) {
 				p->type = P_MA;
 				h->event_dialog.ma = p;
 			}
 			break;
2eb780d7
 	}
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Try to find out parameter name, recognized parameters
524b4677
  * are q, expires and methods
420b6bb0
  */
08327019
 static inline void parse_contact_class(param_hooks_t *_h, param_t *_p)
420b6bb0
 {
80a4d8cd
 
08327019
 	if(!_p->name.s) {
 		LM_ERR("empty value\n");
80a4d8cd
 		return;
 	}
08327019
 	if(!_h) {
 		LM_CRIT("BUG: NULL param hook pointer\n");
2cc36bfc
 		return;
 	}
420b6bb0
 	switch(_p->name.s[0]) {
08327019
 		case 'q':
 		case 'Q':
 			if(_p->name.len == 1) {
 				_p->type = P_Q;
 				_h->contact.q = _p;
 			}
 			break;
 
 		case 'e':
 		case 'E':
 			if((_p->name.len == 7)
 					&& (!strncasecmp(_p->name.s + 1, "xpires", 6))) {
 				_p->type = P_EXPIRES;
 				_h->contact.expires = _p;
 			}
 			break;
 
 		case 'm':
 		case 'M':
 			if((_p->name.len == 7)
 					&& (!strncasecmp(_p->name.s + 1, "ethods", 6))) {
 				_p->type = P_METHODS;
 				_h->contact.methods = _p;
 			}
 			break;
 
 		case 'r':
 		case 'R':
 			if((_p->name.len == 8)
 					&& (!strncasecmp(_p->name.s + 1, "eceived", 7))) {
 				_p->type = P_RECEIVED;
 				_h->contact.received = _p;
 			} else if((_p->name.len == 6)
 						&& (!strncasecmp(_p->name.s + 1, "eg-id", 5))) {
 				_p->type = P_REG_ID;
 				_h->contact.reg_id = _p;
 			}
 			break;
 		case '+':
 			if((_p->name.len == 13)
 					&& (!strncasecmp(_p->name.s + 1, "sip.instance", 12))) {
 				_p->type = P_INSTANCE;
 				_h->contact.instance = _p;
 			}
 			break;
 		case 'o':
 		case 'O':
 			if((_p->name.len == 2) && (!strncasecmp(_p->name.s + 1, "b", 1))) {
 				_p->type = P_OB;
 				_h->contact.ob = _p;
 			}
 			break;
5af1a114
 		case 'f':
 		case 'F':
 			if ((_p->name.len == 5) &&
 				(!strncasecmp(_p->name.s + 1, "lags", 4))) {
 				_p->type = P_FLAGS;
 				_h->contact.flags = _p;
 			}
 			break;
420b6bb0
 	}
 }
 
 
1eb01c66
 /*! \brief
ed49bd28
  * Try to find out parameter name, recognized parameters
  * are transport, lr, r2, maddr
  */
08327019
 static inline void parse_uri_class(param_hooks_t *_h, param_t *_p)
ed49bd28
 {
80a4d8cd
 
08327019
 	if(!_p->name.s) {
 		LM_ERR("empty value\n");
80a4d8cd
 		return;
 	}
08327019
 	if(!_h) {
 		LM_CRIT("BUG: NULL param hook pointer\n");
2cc36bfc
 		return;
 	}
ed49bd28
 	switch(_p->name.s[0]) {
08327019
 		case 't':
 		case 'T':
 			if((_p->name.len == 9)
 					&& (!strncasecmp(_p->name.s + 1, "ransport", 8))) {
 				_p->type = P_TRANSPORT;
 				_h->uri.transport = _p;
 			} else if(_p->name.len == 2) {
 				if(((_p->name.s[1] == 't') || (_p->name.s[1] == 'T'))
 						&& ((_p->name.s[2] == 'l') || (_p->name.s[2] == 'L'))) {
 					_p->type = P_TTL;
 					_h->uri.ttl = _p;
 				}
0b5f5b2e
 			}
08327019
 			break;
 
 		case 'l':
 		case 'L':
 			if((_p->name.len == 2)
 					&& ((_p->name.s[1] == 'r') || (_p->name.s[1] == 'R'))) {
 				_p->type = P_LR;
 				_h->uri.lr = _p;
 			}
 			break;
ed49bd28
 
08327019
 		case 'r':
 		case 'R':
 			if((_p->name.len == 2) && (_p->name.s[1] == '2')) {
 				_p->type = P_R2;
 				_h->uri.r2 = _p;
 			}
 			break;
 
 		case 'm':
 		case 'M':
 			if((_p->name.len == 5)
 					&& (!strncasecmp(_p->name.s + 1, "addr", 4))) {
 				_p->type = P_MADDR;
 				_h->uri.maddr = _p;
 			}
 			break;
 
 		case 'd':
 		case 'D':
 			if((_p->name.len == 5)
 					&& (!strncasecmp(_p->name.s + 1, "stip", 4))) {
 				_p->type = P_DSTIP;
 				_h->uri.dstip = _p;
 			} else if((_p->name.len == 7)
 						&& (!strncasecmp(_p->name.s + 1, "stport", 6))) {
 				_p->type = P_DSTPORT;
 				_h->uri.dstport = _p;
 			}
 			break;
 		case 'f':
 		case 'F':
 			if((_p->name.len == 4)
 					&& (!strncasecmp(_p->name.s + 1, "tag", 3))) {
 				_p->type = P_FTAG;
 				_h->uri.ftag = _p;
 			}
 			break;
 		case 'o':
 		case 'O':
 			if((_p->name.len == 2) && (!strncasecmp(_p->name.s + 1, "b", 1))) {
 				_p->type = P_OB;
 				_h->uri.ob = _p;
 			}
 			break;
ed49bd28
 	}
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse quoted string in a parameter body
  * return the string without quotes in _r
  * parameter and update _s to point behind the
  * closing quote
  */
08327019
 static inline int parse_quoted_param(str *_s, str *_r)
420b6bb0
 {
08327019
 	char *end_quote;
1a5b6cd1
 	char quote;
420b6bb0
 
08327019
 	/* The string must have at least
 	 * surrounding quotes
 	 */
 	if(_s->len < 2) {
420b6bb0
 		return -1;
 	}
 
08327019
 	/* Store the kind of quoting (single or double)
 	 * which we're handling with
 	 */
1a5b6cd1
 	quote = (_s->s)[0];
 
08327019
 	/* Skip opening quote */
420b6bb0
 	_s->s++;
 	_s->len--;
 
 
08327019
 	/* Find closing quote */
1a5b6cd1
 	end_quote = q_memchr(_s->s, quote, _s->len);
420b6bb0
 
08327019
 	/* Not found, return error */
 	if(!end_quote) {
420b6bb0
 		return -2;
 	}
 
08327019
 	/* Let _r point to the string without
 	 * surrounding quotes
 	 */
420b6bb0
 	_r->s = _s->s;
 	_r->len = end_quote - _s->s;
 
08327019
 	/* Update _s parameter to point
 	 * behind the closing quote
 	 */
420b6bb0
 	_s->len -= (end_quote - _s->s + 1);
 	_s->s = end_quote + 1;
 
08327019
 	/* Everything went OK */
420b6bb0
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse unquoted token in a parameter body
  * let _r point to the token and update _s
  * to point right behind the token
  */
08327019
 static inline int parse_token_param(str *_s, str *_r, char separator)
420b6bb0
 {
 	int i;
 
08327019
 	/* There is nothing to parse,
 	 * return error
 	 */
 	if(_s->len == 0) {
420b6bb0
 		return -1;
 	}
 
08327019
 	/* Save the begining of the
 	 * token in _r->s
 	 */
420b6bb0
 	_r->s = _s->s;
 
08327019
 	/* Iterate through the
 	 * token body
 	 */
420b6bb0
 	for(i = 0; i < _s->len; i++) {
 
08327019
 		/* All these characters
 		 * mark end of the token
 		 */
420b6bb0
 		switch(_s->s[i]) {
08327019
 			case ' ':
 			case '\t':
 			case '\r':
 			case '\n':
 			case ',':
 				/* So if you find
 				 * any of them
 				 * stop iterating
 				 */
e0abe290
 				goto out;
08327019
 			default:
 				if(_s->s[i] == separator)
 					goto out;
420b6bb0
 		}
 	}
08327019
 out:
 	if(i == 0) {
420b6bb0
 		return -1;
08327019
 	}
420b6bb0
 
08327019
 	/* Save length of the token */
 	_r->len = i;
420b6bb0
 
08327019
 	/* Update _s parameter so it points
 	 * right behind the end of the token
 	 */
420b6bb0
 	_s->s = _s->s + i;
 	_s->len -= i;
 
08327019
 	/* Everything went OK */
420b6bb0
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse a parameter name
  */
08327019
 static inline void parse_param_name(
 		str *_s, pclass_t _c, param_hooks_t *_h, param_t *_p, char separator)
420b6bb0
 {
80a4d8cd
 
08327019
 	if(!_s->s) {
 		LM_DBG("empty parameter\n");
80a4d8cd
 		return;
 	}
 
420b6bb0
 	_p->name.s = _s->s;
 
 	while(_s->len) {
 		switch(_s->s[0]) {
08327019
 			case ' ':
 			case '\t':
 			case '\r':
 			case '\n':
 			case ',':
 			case '=':
e0abe290
 				goto out;
08327019
 			default:
 				if(_s->s[0] == separator)
 					goto out;
420b6bb0
 		}
 		_s->s++;
 		_s->len--;
 	}
 
08327019
 out:
420b6bb0
 	_p->name.len = _s->s - _p->name.s;
2cc36bfc
 
420b6bb0
 	switch(_c) {
08327019
 		case CLASS_CONTACT:
 			parse_contact_class(_h, _p);
 			break;
 		case CLASS_URI:
 			parse_uri_class(_h, _p);
 			break;
 		case CLASS_EVENT_DIALOG:
 			parse_event_dialog_class(_h, _p);
 			break;
 		default:
 			break;
420b6bb0
 	}
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse body of a parameter. It can be quoted string or
  * a single token.
  */
08327019
 static inline int parse_param_body(str *_s, param_t *_c, char separator)
420b6bb0
 {
08327019
 	if(_s->s[0] == '\"' || _s->s[0] == '\'') {
 		if(parse_quoted_param(_s, &(_c->body)) < 0) {
 			LM_ERR("Error while parsing quoted string\n");
420b6bb0
 			return -2;
 		}
 	} else {
08327019
 		if(parse_token_param(_s, &(_c->body), separator) < 0) {
 			LM_ERR("Error while parsing token\n");
420b6bb0
 			return -3;
 		}
 	}
 
 	return 0;
 }
 
 
1eb01c66
 /*!  \brief
c91680f4
  * Only parse one parameter
  * Returns:
  * 	t: out parameter
  * 	-1: on error
  * 	0: success, but expect a next paramter
59538b66
  * 	1: success and expect no more parameters
c91680f4
  */
08327019
 static inline int parse_param2(
 		str *_s, pclass_t _c, param_hooks_t *_h, param_t *t, char separator)
c91680f4
 {
 	memset(t, 0, sizeof(param_t));
 
e0abe290
 	parse_param_name(_s, _c, _h, t, separator);
c91680f4
 	trim_leading(_s);
08327019
 
 	if(_s->len == 0) { /* The last parameter without body */
c91680f4
 		t->len = t->name.len;
 		goto ok;
 	}
08327019
 
 	if(_s->s[0] == '=') {
c91680f4
 		_s->s++;
 		_s->len--;
 		trim_leading(_s);
 
08327019
 		if(_s->len == 0) {
 			/* Be forgiving and accept parameters with missing value,
 			 * we just set the length of parameter body to 0. */
 			t->body.s = _s->s;
 			t->body.len = 0;
 		} else if(parse_param_body(_s, t, separator) < 0) {
 			LM_ERR("Error while parsing param body\n");
c91680f4
 			goto error;
 		}
 
 		t->len = _s->s - t->name.s;
 
 		trim_leading(_s);
08327019
 		if(_s->len == 0) {
c91680f4
 			goto ok;
 		}
 	} else {
 		t->len = t->name.len;
 	}
 
08327019
 	if(_s->s[0] == ',')
 		goto ok; /* To be able to parse header parameters */
 	if(_s->s[0] == '>')
 		goto ok; /* To be able to parse URI parameters */
c91680f4
 
08327019
 	if(_s->s[0] != separator) {
a2511df2
 		LM_ERR("Invalid character '%c' - expected '%c'\n", _s->s[0], separator);
c91680f4
 		goto error;
 	}
 
 	_s->s++;
 	_s->len--;
 	trim_leading(_s);
08327019
 
 	if(_s->len == 0) {
 		LM_ERR("Param name missing after %c\n", separator);
c91680f4
 		goto error;
 	}
 
 	return 0; /* expect more params */
 
 ok:
 	return 1; /* done with parsing for params */
 error:
 	return -1;
 }
 
e0abe290
 /*!  \brief
  * Only parse one parameter
  * Returns:
  * 	t: out parameter
  * 	-1: on error
  * 	0: success, but expect a next paramter
59538b66
  * 	1: success and expect no more parameters
e0abe290
  */
e3a8e577
 int parse_param(str *_s, pclass_t _c, param_hooks_t *_h, param_t *t)
e0abe290
 {
 	return parse_param2(_s, _c, _h, t, ';');
 }
c91680f4
 
1eb01c66
 /*! \brief
420b6bb0
  * Parse parameters
7af69774
  * \param _s is string containing parameters, it will be updated to point behind the parameters
  * \param _c is class of parameters
  * \param _h is pointer to structure that will be filled with pointer to well known parameters
  * \param _p pointing to linked list where parsed parameters will be stored
  * \return 0 on success and negative number on an error
420b6bb0
  */
08327019
 int parse_params(str *_s, pclass_t _c, param_hooks_t *_h, param_t **_p)
e0abe290
 {
 	return parse_params2(_s, _c, _h, _p, ';');
 }
 
 /*! \brief
  * Parse parameters with configurable separator
  * \param _s is string containing parameters, it will be updated to point behind the parameters
  * \param _c is class of parameters
  * \param _h is pointer to structure that will be filled with pointer to well known parameters
  * \param _p pointing to linked list where parsed parameters will be stored
  * \param separator single character separator
  * \return 0 on success and negative number on an error
  */
08327019
 int parse_params2(
 		str *_s, pclass_t _c, param_hooks_t *_h, param_t **_p, char separator)
420b6bb0
 {
08327019
 	param_t *t;
420b6bb0
 
08327019
 	if(!_s || !_p) {
 		LM_ERR("Invalid parameter value\n");
420b6bb0
 		return -1;
 	}
 
08327019
 	if(_h)
2cc36bfc
 		memset(_h, 0, sizeof(param_hooks_t));
afc00488
 	*_p = 0;
ddda1e10
 
76e6033f
 	if(!_s->s || _s->len<=0) { /* no parameters at all -- we're done */
08327019
 		LM_DBG("empty uri params, skipping\n");
80a4d8cd
 		return 0;
 	}
08327019
 
420b6bb0
 	while(1) {
08327019
 		t = (param_t *)pkg_malloc(sizeof(param_t));
 		if(t == 0) {
e3ecad34
 			PKG_MEM_ERROR;
420b6bb0
 			goto error;
 		}
 
e0abe290
 		switch(parse_param2(_s, _c, _h, t, separator)) {
08327019
 			case 0:
 				break;
 			case 1:
 				goto ok;
 			default:
 				goto error;
420b6bb0
 		}
 
 		t->next = *_p;
 		*_p = t;
 	}
 
08327019
 error:
 	if(t)
 		pkg_free(t);
420b6bb0
 	free_params(*_p);
b12c2df6
 	*_p = 0;
420b6bb0
 	return -2;
 
08327019
 ok:
420b6bb0
 	t->next = *_p;
 	*_p = t;
 	return 0;
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Free linked list of parameters
  */
08327019
 static inline void do_free_params(param_t *_p, int _shm)
420b6bb0
 {
08327019
 	param_t *ptr;
 
420b6bb0
 	while(_p) {
 		ptr = _p;
 		_p = _p->next;
08327019
 		if(_shm)
 			shm_free(ptr);
 		else
 			pkg_free(ptr);
 	}
91804f9b
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Free linked list of parameters
  */
08327019
 void free_params(param_t *_p)
91804f9b
 {
 	do_free_params(_p, 0);
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Free linked list of parameters
  */
08327019
 void shm_free_params(param_t *_p)
91804f9b
 {
 	do_free_params(_p, 1);
420b6bb0
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Print a parameter structure, just for debugging
  */
08327019
 static inline void print_param(FILE *_o, param_t *_p)
420b6bb0
 {
08327019
 	char *type;
420b6bb0
 
1c6135b6
 	fprintf(_o, "---param(%p)---\n", _p);
08327019
 
420b6bb0
 	switch(_p->type) {
08327019
 		case P_OTHER:
 			type = "P_OTHER";
 			break;
 		case P_Q:
 			type = "P_Q";
 			break;
 		case P_EXPIRES:
 			type = "P_EXPIRES";
 			break;
 		case P_METHODS:
 			type = "P_METHODS";
 			break;
5af1a114
 		case P_FLAGS:
 			type = "P_FLAGS";
 			break;
08327019
 		case P_TRANSPORT:
 			type = "P_TRANSPORT";
 			break;
 		case P_LR:
 			type = "P_LR";
 			break;
 		case P_R2:
 			type = "P_R2";
 			break;
 		case P_MADDR:
 			type = "P_MADDR";
 			break;
 		case P_TTL:
 			type = "P_TTL";
 			break;
 		case P_RECEIVED:
 			type = "P_RECEIVED";
 			break;
 		case P_DSTIP:
 			type = "P_DSTIP";
 			break;
 		case P_DSTPORT:
 			type = "P_DSTPORT";
 			break;
 		case P_INSTANCE:
 			type = "P_INSTANCE";
 			break;
 		case P_FTAG:
 			type = "P_FTAG";
 			break;
 		case P_CALL_ID:
 			type = "P_CALL_ID";
 			break;
 		case P_FROM_TAG:
 			type = "P_FROM_TAG";
 			break;
 		case P_TO_TAG:
 			type = "P_TO_TAG";
 			break;
 		case P_ISD:
 			type = "P_ISD";
 			break;
 		case P_SLA:
 			type = "P_SLA";
 			break;
 		default:
 			type = "UNKNOWN";
 			break;
420b6bb0
 	}
08327019
 
1c6135b6
 	fprintf(_o, "type: %s\n", type);
 	fprintf(_o, "name: \'%.*s\'\n", _p->name.len, _p->name.s);
 	fprintf(_o, "body: \'%.*s\'\n", _p->body.len, _p->body.s);
 	fprintf(_o, "len : %d\n", _p->len);
 	fprintf(_o, "---/param---\n");
420b6bb0
 }
 
 
1eb01c66
 /*! \brief
420b6bb0
  * Print linked list of parameters, just for debugging
  */
08327019
 void print_params(FILE *_o, param_t *_p)
420b6bb0
 {
08327019
 	param_t *ptr;
 
420b6bb0
 	ptr = _p;
 	while(ptr) {
1c6135b6
 		print_param(_o, ptr);
420b6bb0
 		ptr = ptr->next;
 	}
 }
5d17eb17
 
 
1eb01c66
 /*! \brief
5d17eb17
  * Duplicate linked list of parameters
  */
08327019
 static inline int do_duplicate_params(param_t **_n, param_t *_p, int _shm)
5d17eb17
 {
08327019
 	param_t *last, *ptr, *t;
5d17eb17
 
08327019
 	if(!_n) {
 		LM_ERR("Invalid parameter value\n");
5d17eb17
 		return -1;
 	}
08327019
 
5d17eb17
 	last = 0;
 	*_n = 0;
 	ptr = _p;
 	while(ptr) {
08327019
 		if(_shm) {
 			t = (param_t *)shm_malloc(sizeof(param_t));
91804f9b
 		} else {
08327019
 			t = (param_t *)pkg_malloc(sizeof(param_t));
91804f9b
 		}
08327019
 		if(!t) {
e3ecad34
 			if (_shm) {
 				SHM_MEM_ERROR;
 			} else {
 				PKG_MEM_ERROR;
 			}
5d17eb17
 			goto err;
 		}
 		memcpy(t, ptr, sizeof(param_t));
 		t->next = 0;
 
08327019
 		if(!*_n)
 			*_n = t;
 		if(last)
 			last->next = t;
5d17eb17
 		last = t;
 
 		ptr = ptr->next;
 	}
bfcd8364
 	return 0;
5d17eb17
 
08327019
 err:
91804f9b
 	do_free_params(*_n, _shm);
bfcd8364
 	return -2;
5d17eb17
 }
91804f9b
 
 
1eb01c66
 /*! \brief
91804f9b
  * Duplicate linked list of parameters
  */
08327019
 int duplicate_params(param_t **_n, param_t *_p)
91804f9b
 {
 	return do_duplicate_params(_n, _p, 0);
 }
 
 
1eb01c66
 /*! \brief
91804f9b
  * Duplicate linked list of parameters
  */
08327019
 int shm_duplicate_params(param_t **_n, param_t *_p)
91804f9b
 {
 	return do_duplicate_params(_n, _p, 1);
 }