/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Note: These *must* be in the same order as the enum types. */ const sdp_namearray_t sdp_media[SDP_MAX_MEDIA_TYPES] =
{
{"audio", sizeof("audio")},
{"video", sizeof("video")},
{"application", sizeof("application")},
{"data", sizeof("data")},
{"control", sizeof("control")},
{"nas/radius", sizeof("nas/radius")},
{"nas/tacacs", sizeof("nas/tacacs")},
{"nas/diameter", sizeof("nas/diameter")},
{"nas/l2tp", sizeof("nas/l2tp")},
{"nas/login", sizeof("nas/login")},
{"nas/none", sizeof("nas/none")},
{"image", sizeof("image")},
{"text", sizeof("text")}
};
/* Note: These *must* be in the same order as the enum types. */ const sdp_namearray_t sdp_nettype[SDP_MAX_NETWORK_TYPES] =
{
{"IN", sizeof("IN")},
{"ATM", sizeof("ATM")},
{"FR", sizeof("FR")},
{"LOCAL", sizeof("LOCAL")}
};
/* Note: These *must* be in the same order as the enum types. */ const sdp_namearray_t sdp_addrtype[SDP_MAX_ADDR_TYPES] =
{
{"IP4", sizeof("IP4")},
{"IP6", sizeof("IP6")},
{"NSAP", sizeof("NSAP")},
{"EPN", sizeof("EPN")},
{"E164", sizeof("E164")},
{"GWID", sizeof("GWID")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_transport[SDP_MAX_TRANSPORT_TYPES] =
{
{"RTP/AVP", sizeof("RTP/AVP")},
{"udp", sizeof("udp")},
{"udptl", sizeof("udptl")},
{"ces10", sizeof("ces10")},
{"LOCAL", sizeof("LOCAL")},
{"AAL2/ITU", sizeof("AAL2/ITU")},
{"AAL2/ATMF", sizeof("AAL2/ATMF")},
{"AAL2/custom", sizeof("AAL2/custom")},
{"AAL1/AVP", sizeof("AAL1/AVP")},
{"udpsprt", sizeof("udpsprt")},
{"RTP/SAVP", sizeof("RTP/SAVP")},
{"tcp", sizeof("tcp")},
{"RTP/SAVPF", sizeof("RTP/SAVPF")},
{"DTLS/SCTP", sizeof("DTLS/SCTP")},
{"RTP/AVPF", sizeof("RTP/AVPF")},
{"UDP/TLS/RTP/SAVP", sizeof("UDP/TLS/RTP/SAVP")},
{"UDP/TLS/RTP/SAVPF", sizeof("UDP/TLS/RTP/SAVPF")},
{"TCP/DTLS/RTP/SAVP", sizeof("TCP/DTLS/RTP/SAVP")},
{"TCP/DTLS/RTP/SAVPF", sizeof("TCP/DTLS/RTP/SAVPF")},
{"UDP/DTLS/SCTP", sizeof("UDP/DTLS/SCTP")},
{"TCP/DTLS/SCTP", sizeof("TCP/DTLS/SCTP")},
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_encrypt[SDP_MAX_ENCRYPT_TYPES] =
{
{"clear", sizeof("clear")},
{"base64", sizeof("base64")},
{"uri", sizeof("uri")},
{"prompt", sizeof("prompt")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_payload[SDP_MAX_STRING_PAYLOAD_TYPES] =
{
{"t38", sizeof("t38")},
{"X-tmr", sizeof("X-tmr")},
{"T120", sizeof("T120")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_t38_rate[SDP_T38_MAX_RATES] =
{
{"localTCF", sizeof("localTCF")},
{"transferredTCF", sizeof("transferredTCF")},
{"unknown", sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_t38_udpec[SDP_T38_MAX_UDPEC] =
{
{"t38UDPRedundancy", sizeof("t38UDPRedundancy")},
{"t38UDPFEC", sizeof("t38UDPFEC")},
{"unknown", sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_qos_strength[SDP_MAX_QOS_STRENGTH] =
{
{"optional", sizeof("optional")},
{"mandatory", sizeof("mandatory")},
{"success", sizeof("success")},
{"failure", sizeof("failure")},
{"none", sizeof("none")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_qos_status_type[SDP_MAX_QOS_STATUS_TYPES] =
{
{"local", sizeof("local")},
{"remote", sizeof("remote")},
{"e2e", sizeof("e2e")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_curr_type[SDP_MAX_CURR_TYPES] =
{
{"qos", sizeof("qos")},
{"unknown", sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_des_type[SDP_MAX_DES_TYPES] =
{
{"qos", sizeof("qos")},
{"unknown", sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_conf_type[SDP_MAX_CONF_TYPES] =
{
{"qos", sizeof("qos")},
{"unknown", sizeof("unknown")}
}; /* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_qos_direction[SDP_MAX_QOS_DIR] =
{
{"send", sizeof("send")},
{"recv", sizeof("recv")},
{"sendrecv", sizeof("sendrecv")},
{"none", sizeof("none")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_silencesupp_pref[SDP_MAX_SILENCESUPP_PREF] = {
{"standard", sizeof("standard")},
{"custom", sizeof("custom")},
{"-", sizeof("-")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_silencesupp_siduse[SDP_MAX_SILENCESUPP_SIDUSE] = {
{"No SID", sizeof("No SID")},
{"Fixed Noise", sizeof("Fixed Noise")},
{"Sampled Noise", sizeof("Sampled Noise")},
{"-", sizeof("-")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_mediadir_role[SDP_MAX_MEDIADIR_ROLES] =
{
{"passive", sizeof("passive")},
{"active", sizeof("active")},
{"both", sizeof("both")},
{"reuse", sizeof("reuse")},
{"unknown", sizeof("unknown")}
};
/* Note: These *must* be in the same order as the enum type. */ const sdp_namearray_t sdp_fmtp_codec_param_val[SDP_MAX_FMTP_PARAM_VAL] =
{
{"yes", sizeof("yes")},
{"no", sizeof("no")}
};
/* Maintain the same order as defined in typedef sdp_src_filter_mode_e */ const sdp_namearray_t sdp_src_filter_mode_val[SDP_MAX_FILTER_MODE] =
{
{"incl", sizeof("incl")},
{"excl", sizeof("excl")}
};
/* Maintain the same order as defined in typdef sdp_rtcp_unicast_mode_e */ const sdp_namearray_t sdp_rtcp_unicast_mode_val[SDP_RTCP_MAX_UNICAST_MODE] =
{
{"reflection", sizeof("reflection")},
{"rsi", sizeof("rsi")}
};
#define SDP_NAME(x) {x, sizeof(x)} /* Maintain the same order as defined in typdef sdp_rtcp_fb_type_e */ const sdp_namearray_t sdp_rtcp_fb_type_val[SDP_MAX_RTCP_FB] =
{
SDP_NAME("ack"),
SDP_NAME("ccm"),
SDP_NAME("nack"),
SDP_NAME("trr-int"),
SDP_NAME("goog-remb"),
SDP_NAME("transport-cc")
};
/* Maintain the same order as defined in typdef sdp_rtcp_fb_nack_type_e */ const sdp_namearray_t sdp_rtcp_fb_nack_type_val[SDP_MAX_RTCP_FB_NACK] =
{
SDP_NAME(""),
SDP_NAME("sli"),
SDP_NAME("pli"),
SDP_NAME("rpsi"),
SDP_NAME("app"),
SDP_NAME("rai"),
SDP_NAME("tllei"),
SDP_NAME("pslei"),
SDP_NAME("ecn")
};
/* Maintain the same order as defined in typdef sdp_rtcp_fb_ack_type_e */ const sdp_namearray_t sdp_rtcp_fb_ack_type_val[SDP_MAX_RTCP_FB_ACK] =
{
SDP_NAME("rpsi"),
SDP_NAME("app")
};
/* Maintain the same order as defined in typdef sdp_rtcp_fb_ccm_type_e */ const sdp_namearray_t sdp_rtcp_fb_ccm_type_val[SDP_MAX_RTCP_FB_CCM] =
{
SDP_NAME("fir"),
SDP_NAME("tmmbr"),
SDP_NAME("tstr"),
SDP_NAME("vbcm")
};
/* Maintain the same order as defined in typedef sdp_setup_type_e */ const sdp_namearray_t sdp_setup_type_val[SDP_MAX_SETUP] =
{
SDP_NAME("active"),
SDP_NAME("passive"),
SDP_NAME("actpass"),
SDP_NAME("holdconn")
};
/* Maintain the same order as defined in typedef sdp_connection_type_e */ const sdp_namearray_t sdp_connection_type_val[SDP_MAX_CONNECTION] =
{
SDP_NAME("new"),
SDP_NAME("existing")
};
/* Maintain same order as defined in typedef sdp_srtp_crypto_suite_t */ const sdp_srtp_crypto_suite_list sdp_srtp_crypto_suite_array[SDP_SRTP_MAX_NUM_CRYPTO_SUITES] =
{
{SDP_SRTP_UNKNOWN_CRYPTO_SUITE, UNKNOWN_CRYPTO_SUITE, 0, 0},
{SDP_SRTP_AES_CM_128_HMAC_SHA1_32, AES_CM_128_HMAC_SHA1_32,
SDP_SRTP_AES_CM_128_HMAC_SHA1_32_KEY_BYTES,
SDP_SRTP_AES_CM_128_HMAC_SHA1_32_SALT_BYTES},
{SDP_SRTP_AES_CM_128_HMAC_SHA1_80, AES_CM_128_HMAC_SHA1_80,
SDP_SRTP_AES_CM_128_HMAC_SHA1_80_KEY_BYTES,
SDP_SRTP_AES_CM_128_HMAC_SHA1_80_SALT_BYTES},
{SDP_SRTP_F8_128_HMAC_SHA1_80, F8_128_HMAC_SHA1_80,
SDP_SRTP_F8_128_HMAC_SHA1_80_KEY_BYTES,
SDP_SRTP_F8_128_HMAC_SHA1_80_SALT_BYTES}
};
/* Function: sdp_init_description * Description: Allocates a new SDP structure that can be used for either * parsing or building an SDP description. This routine * saves the config pointer passed in the SDP structure so * SDP will know how to parse/build based on the options defined. * An SDP structure must be allocated before parsing or building * since the handle must be passed to these routines. * Parameters: config_p The config handle returned by sdp_init_config * Returns: A handle for a new SDP structure as a void ptr.
*/
sdp_t *sdp_init_description (sdp_conf_options_t *conf_p)
{ int i;
sdp_t *sdp_p;
/* Set default debug flags from application config. */ for (i=0; i < SDP_MAX_DEBUG_TYPES; i++) {
sdp_p->debug_flag[i] = conf_p->debug_flag[i];
}
return (sdp_p);
}
/* Function: void sdp_debug(sdp_t *sdp_p, sdp_debug_e debug_type, * tinybool my_bool); * Description: Define the type of debug for this particular SDP structure. * By default, each SDP description has the settings that are * set for the application. * Valid debug types are ERRORS, WARNINGS, and TRACE. Each * debug type can be turned on/off individually. The * debug level can be redefined at any time. * Parameters: sdp_ptr The SDP handle returned by sdp_init_description. * debug_type Specifies the debug type being enabled/disabled. * my_bool Defines whether the debug should be enabled or not. * Returns: Nothing.
*/ void sdp_debug (sdp_t *sdp_p, sdp_debug_e debug_type, tinybool debug_flag)
{ if (!sdp_p) { return;
}
if (debug_type < SDP_MAX_DEBUG_TYPES) {
sdp_p->debug_flag[debug_type] = debug_flag;
}
}
/* Function: void sdp_set_string_debug(sdp_t *sdp_p, char *debug_str) * Description: Define a string to be associated with all debug output * for this SDP. The string will be copied into the SDP * structure and so the library will not be dependent on * the application's memory for this string. * Parameters: sdp_p The SDP handle returned by sdp_init_description. * debug_str Pointer to a string that should be printed out * with every debug msg. * Returns: Nothing.
*/ void sdp_set_string_debug (sdp_t *sdp_p, constchar *debug_str)
{ if (!sdp_p) { return;
}
/* Function: sdp_validate_sdp * Description: Validate an SDP structure. * Parameters: sdp_p The SDP handle of the struct to validate. * Returns: A result value indicating if the validation was successful. * If not, what type of error was encountered.
*/
sdp_result_e sdp_validate_sdp (sdp_t *sdp_p)
{ int i;
uint16_t num_media_levels;
/* Need to validate c= info is specified at session level or * at all m= levels.
*/ if (sdp_connection_valid((void *)sdp_p, SDP_SESSION_LEVEL) == FALSE) {
num_media_levels = sdp_get_num_media_lines((void *)sdp_p); for (i=1; i <= num_media_levels; i++) { if (sdp_connection_valid((void *)sdp_p, (unsignedshort)i) == FALSE) {
sdp_parse_error(sdp_p, "%s c= connection line not specified for " "every media level, validation failed.",
sdp_p->debug_str); return (SDP_FAILURE);
}
}
}
/* Validate required lines were specified */ if ((sdp_owner_valid((void *)sdp_p) == FALSE) &&
(sdp_p->conf_p->owner_reqd == TRUE)) {
sdp_parse_error(sdp_p, "%s o= owner line not specified, validation failed.",
sdp_p->debug_str); return (SDP_FAILURE);
}
if ((sdp_session_name_valid((void *)sdp_p) == FALSE) &&
(sdp_p->conf_p->session_name_reqd == TRUE)) {
sdp_parse_error(sdp_p, "%s s= session name line not specified, validation failed.",
sdp_p->debug_str); return (SDP_FAILURE);
}
if ((sdp_timespec_valid((void *)sdp_p) == FALSE) &&
(sdp_p->conf_p->timespec_reqd == TRUE)) {
sdp_parse_error(sdp_p, "%s t= timespec line not specified, validation failed.",
sdp_p->debug_str); return (SDP_FAILURE);
}
return (SDP_SUCCESS);
}
/* Function: sdp_parse * Description: Parse an SDP description in the specified buffer. * Parameters: sdp_p The SDP handle returned by sdp_init_description * bufp Pointer to the buffer containing the SDP * description to parse. * len The length of the buffer. * Returns: A result value indicating if the parse was successful and * if not, what type of error was encountered. The * information from the parse is stored in the sdp_p structure.
*/
sdp_result_e sdp_parse (sdp_t *sdp_p, constchar *buf, size_t len)
{
uint8_t i;
uint16_t cur_level = SDP_SESSION_LEVEL; constchar *ptr; constchar *next_ptr = NULL; char *line_end;
sdp_token_e last_token = SDP_TOKEN_V;
sdp_result_e result = SDP_SUCCESS;
tinybool parse_done = FALSE;
tinybool end_found = FALSE;
tinybool first_line = TRUE;
tinybool unrec_token = FALSE; constchar **bufp = &buf;
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Trace SDP Parse:", sdp_p->debug_str);
}
next_ptr = *bufp;
sdp_p->conf_p->num_parses++;
/* Initialize the last valid capability instance to zero. Used
* to help in parsing X-cpar attrs. */
sdp_p->cap_valid = FALSE;
sdp_p->last_cap_inst = 0;
sdp_p->parse_line = 0;
/* We want to try to find the end of the SDP description, even if * we find a parsing error.
*/ while (!end_found) { /* If the last char of this line goes beyond the end of the buffer, * we don't parse it.
*/
ptr = next_ptr;
sdp_p->parse_line++;
line_end = sdp_findchar(ptr, "\n"); if ((line_end >= (*bufp + len)) ||
(*line_end == '\0')) { /* As this does not update the result value the SDP up to this point * is still accept as valid. So encountering this is not treated as * an error.
*/
sdp_parse_error(sdp_p, "%s End of line beyond end of buffer.",
sdp_p->debug_str);
SDPLogError(logTag, "SDP: Invalid SDP, no \\n (len %u): %*s",
(unsigned)len, (int)len, *bufp);
end_found = TRUE; break;
}
/* Print the line if we're tracing. */ if ((parse_done == FALSE) &&
(sdp_p->debug_flag[SDP_DEBUG_TRACE])) {
SDP_PRINT("%s ", sdp_p->debug_str);
SDP_PRINT("%*s", (int)(line_end - ptr), ptr);
}
/* Find out which token this line has, if any. */ for (i=0; i < SDP_MAX_TOKENS; i++) { if (strncmp(ptr, sdp_token[i].name, SDP_TOKEN_LEN) == 0) { break;
}
} if (i == SDP_MAX_TOKENS) { /* See if the second char on the next line is an '=' char.
* If so, we note this as an unrecognized token line. */ if (ptr[1] == '=') {
unrec_token = TRUE;
} if (first_line == TRUE) {
sdp_parse_error(sdp_p, "%s Attempt to parse text not recognized as " "SDP text, parse fails.", sdp_p->debug_str); /* If we haven't already printed out the line we * were trying to parse, do it now.
*/ if (!sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s ", sdp_p->debug_str);
SDP_PRINT("%*s", (int)(line_end - ptr), ptr);
}
sdp_p->conf_p->num_not_sdp_desc++; return (SDP_NOT_SDP_DESCRIPTION);
} else {
end_found = TRUE; break;
}
}
/* This is the beginning of a new SDP description. */ if ((first_line != TRUE) && (i == SDP_TOKEN_V)) {
end_found = TRUE; break;
}
/* Advance the next ptr to one char beyond the end of the line. */
next_ptr = line_end + 1; if (next_ptr >= (*bufp + len)) {
end_found = TRUE;
}
/* If we've finished parsing and are just looking for the end of * the SDP description, we don't need to do anything else here.
*/ if (parse_done == TRUE) { continue;
}
/* Only certain tokens are valid at the media level. */ if (cur_level != SDP_SESSION_LEVEL) { if ((i != SDP_TOKEN_I) && (i != SDP_TOKEN_C) &&
(i != SDP_TOKEN_B) && (i != SDP_TOKEN_K) &&
(i != SDP_TOKEN_A) && (i != SDP_TOKEN_M)) {
sdp_p->conf_p->num_invalid_token_order++;
sdp_parse_error(sdp_p, "%s Warning: Invalid token %s found at media level",
sdp_p->debug_str, sdp_token[i].name); continue;
}
}
/* Verify the token ordering. */ if (first_line == TRUE) { if (i != SDP_TOKEN_V) { if (sdp_p->conf_p->version_reqd == TRUE) {
sdp_parse_error(sdp_p, "%s First line not v=, parse fails",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_token_order++;
result = SDP_INVALID_TOKEN_ORDERING;
parse_done = TRUE;
} else {
last_token = (sdp_token_e)i;
}
} else {
last_token = (sdp_token_e)i;
}
first_line = FALSE;
} else { if (i < last_token) {
sdp_p->conf_p->num_invalid_token_order++;
sdp_parse_error(sdp_p, "%s Warning: Invalid token ordering detected, " "token %s found after token %s", sdp_p->debug_str,
sdp_token[i].name, sdp_token[last_token].name);
}
}
/* Finally parse the line. */
ptr += SDP_TOKEN_LEN;
result = sdp_token[i].parse_func(sdp_p, cur_level, (constchar *)ptr);
last_token = (sdp_token_e)i; if (last_token == SDP_TOKEN_M) { if (cur_level == SDP_SESSION_LEVEL) {
cur_level = 1;
} else {
cur_level++;
} /* The token ordering can start again at i= */
last_token = (sdp_token_e)(SDP_TOKEN_I - 1);
} if (result != SDP_SUCCESS) {
parse_done = TRUE;
}
/* Skip the new line char at the end of this line and see if * this is the end of the buffer.
*/ if ((line_end + 1) == (*bufp + len)) {
end_found = TRUE;
}
}
/* If we found no valid lines, return an error. */ if (first_line == TRUE) {
sdp_p->conf_p->num_not_sdp_desc++; return (SDP_NOT_SDP_DESCRIPTION);
}
/* If no errors were found yet, validate the overall sdp. */ if (result == SDP_SUCCESS) {
result = sdp_validate_sdp(sdp_p);
} /* Return the pointer where we left off. */
*bufp = next_ptr; /* If the SDP is valid, but the next line following was an
* unrecognized <token>= line, indicate this on the return. */ if ((result == SDP_SUCCESS) && (unrec_token == TRUE)) { return (SDP_UNRECOGNIZED_TOKEN);
} else { return (result);
}
}
/* Function: sdp_build * Description: Build an SDP description in the specified buffer based * on the information in the given SDP structure. * Parameters: sdp_p The SDP handle returned by sdp_init_description * fs A flex_string where the SDP description should be built. * Returns: A result value indicating if the build was successful and * if not, what type of error was encountered - e.g., * description was too long for the given buffer.
*/
sdp_result_e sdp_build (sdp_t *sdp_p, flex_string *fs)
{ int i, j;
sdp_result_e result = SDP_SUCCESS;
if (!sdp_p) { return (SDP_INVALID_SDP_PTR);
}
if (!fs) { return (SDP_NULL_BUF_PTR);
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Trace SDP Build:", sdp_p->debug_str);
}
sdp_p->conf_p->num_builds++;
for (i=0; ((i < SDP_TOKEN_M) &&
(result == SDP_SUCCESS)); i++) {
result = sdp_token[i].build_func(sdp_p, SDP_SESSION_LEVEL, fs); /* ok not to check buffer space (yet) as the if() checks it */
} /* If the session level was ok, build the media lines. */ if (result == SDP_SUCCESS) { for (i=1; ((i <= sdp_p->mca_count) &&
(result == SDP_SUCCESS)); i++) {
result = sdp_token[SDP_TOKEN_M].build_func(sdp_p, (uint16_t)i, fs);
/* ok not to check buffer space (yet) as the for() checks it */ for (j=SDP_TOKEN_I;
((j < SDP_TOKEN_M) && (result == SDP_SUCCESS));
j++) { if ((j == SDP_TOKEN_U) || (j == SDP_TOKEN_E) ||
(j == SDP_TOKEN_P) || (j == SDP_TOKEN_T) ||
(j == SDP_TOKEN_R) || (j == SDP_TOKEN_Z)) { /* These tokens not valid at media level. */ continue;
}
result = sdp_token[j].build_func(sdp_p, (uint16_t)i, fs); /* ok not to check buffer space (yet) as the for() checks it */
}
}
}
return (result);
}
/* Function: sdp_free_description * Description: Free an SDP description and all memory associated with it. * Parameters: sdp_p The SDP handle returned by sdp_init_description * Returns: A result value indicating if the free was successful and * if not, what type of error was encountered - e.g., sdp_p * was invalid and didn't point to an SDP structure.
*/
sdp_result_e sdp_free_description (sdp_t *sdp_p)
{
sdp_timespec_t *time_p, *next_time_p;
sdp_attr_t *attr_p, *next_attr_p;
sdp_mca_t *mca_p, *next_mca_p;
sdp_bw_t *bw_p;
sdp_bw_data_t *bw_data_p;
if (!sdp_p) { return (SDP_INVALID_SDP_PTR);
}
/* Free the config structure */
sdp_free_config(sdp_p->conf_p);
/* Free any timespec structures - should be only one since * this is all we currently support.
*/
time_p = sdp_p->timespec_p; while (time_p != NULL) {
next_time_p = time_p->next_p;
SDP_FREE(time_p);
time_p = next_time_p;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.