703 lines
16 KiB
C
703 lines
16 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Andrew Thompson <andrew@hijacked.us>
|
|
* Rob Charlton <rob.charlton@savageminds.com>
|
|
* Karl Anderson <karl@2600hz.com>
|
|
*
|
|
* Original from mod_erlang_event.
|
|
* ei_helpers.c -- helper functions for ei
|
|
*
|
|
*/
|
|
#include "mod_kazoo.h"
|
|
|
|
#define kz_resize(l) {\
|
|
char *dp;\
|
|
olen += (len + l + block);\
|
|
cpos = c - data;\
|
|
if ((dp = realloc(data, olen))) {\
|
|
data = dp;\
|
|
c = data + cpos;\
|
|
memset(c, 0, olen - cpos);\
|
|
}} \
|
|
|
|
|
|
void kz_check_set_profile_var(switch_channel_t *channel, char* var, char *val)
|
|
{
|
|
int idx = 0;
|
|
while(kazoo_globals.profile_vars_prefixes[idx] != NULL) {
|
|
char *prefix = kazoo_globals.profile_vars_prefixes[idx];
|
|
if (!strncasecmp(var, prefix, strlen(prefix))) {
|
|
switch_channel_set_profile_var(channel, var + strlen(prefix), val);
|
|
|
|
}
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) kz_switch_core_merge_variables(switch_event_t *event)
|
|
{
|
|
switch_event_t *global_vars;
|
|
switch_status_t status = switch_core_get_variables(&global_vars);
|
|
if(status == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_merge(event, global_vars);
|
|
switch_event_destroy(&global_vars);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) kz_switch_core_base_headers_for_expand(switch_event_t **event)
|
|
{
|
|
switch_status_t status = SWITCH_STATUS_GENERR;
|
|
*event = NULL;
|
|
if(switch_event_create(event, SWITCH_EVENT_GENERAL) == SWITCH_STATUS_SUCCESS) {
|
|
status = kz_switch_core_merge_variables(*event);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) kz_expand_api_execute(const char *cmd, const char *arg, switch_core_session_t *session, switch_stream_handle_t *stream)
|
|
{
|
|
switch_api_interface_t *api;
|
|
switch_status_t status;
|
|
char *arg_used;
|
|
char *cmd_used;
|
|
|
|
switch_assert(stream != NULL);
|
|
switch_assert(stream->data != NULL);
|
|
switch_assert(stream->write_function != NULL);
|
|
|
|
cmd_used = switch_strip_whitespace(cmd);
|
|
arg_used = switch_strip_whitespace(arg);
|
|
|
|
if (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "executing [%s] => [%s]\n", cmd_used, arg_used);
|
|
if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) {
|
|
stream->write_function(stream, "COMMAND RETURNED ERROR!\n");
|
|
}
|
|
UNPROTECT_INTERFACE(api);
|
|
} else {
|
|
status = SWITCH_STATUS_FALSE;
|
|
stream->write_function(stream, "INVALID COMMAND!\n");
|
|
}
|
|
|
|
if (cmd_used != cmd) {
|
|
switch_safe_free(cmd_used);
|
|
}
|
|
|
|
if (arg_used != arg) {
|
|
switch_safe_free(arg_used);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
SWITCH_DECLARE(char *) kz_event_expand_headers_check(switch_event_t *event, const char *in, switch_event_t *var_list, switch_event_t *api_list, uint32_t recur)
|
|
{
|
|
char *p, *c = NULL;
|
|
char *data, *indup, *endof_indup;
|
|
size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128;
|
|
const char *sub_val = NULL;
|
|
char *cloned_sub_val = NULL, *expanded_sub_val = NULL;
|
|
char *func_val = NULL;
|
|
int nv = 0;
|
|
char *gvar = NULL, *sb = NULL;
|
|
|
|
if (recur > 100) {
|
|
return (char *) in;
|
|
}
|
|
|
|
if (zstr(in)) {
|
|
return (char *) in;
|
|
}
|
|
|
|
nv = switch_string_var_check_const(in) || switch_string_has_escaped_data(in);
|
|
|
|
if (!nv) {
|
|
return (char *) in;
|
|
}
|
|
|
|
nv = 0;
|
|
olen = strlen(in) + 1;
|
|
indup = strdup(in);
|
|
switch_assert(indup);
|
|
endof_indup = end_of_p(indup) + 1;
|
|
|
|
if ((data = malloc(olen))) {
|
|
memset(data, 0, olen);
|
|
c = data;
|
|
for (p = indup; p && p < endof_indup && *p; p++) {
|
|
int global = 0;
|
|
vtype = 0;
|
|
|
|
if (*p == '\\') {
|
|
if (*(p + 1) == '$') {
|
|
nv = 1;
|
|
p++;
|
|
if (*(p + 1) == '$') {
|
|
p++;
|
|
}
|
|
} else if (*(p + 1) == '\'') {
|
|
p++;
|
|
continue;
|
|
} else if (*(p + 1) == '\\') {
|
|
if (len + 1 >= olen) {
|
|
kz_resize(1);
|
|
}
|
|
|
|
*c++ = *p++;
|
|
len++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (*p == '$' && !nv) {
|
|
if (*(p + 1) == '$') {
|
|
p++;
|
|
global++;
|
|
}
|
|
|
|
if (*(p + 1)) {
|
|
if (*(p + 1) == '{') {
|
|
vtype = global ? 3 : 1;
|
|
} else {
|
|
nv = 1;
|
|
}
|
|
} else {
|
|
nv = 1;
|
|
}
|
|
}
|
|
|
|
if (nv) {
|
|
if (len + 1 >= olen) {
|
|
kz_resize(1);
|
|
}
|
|
|
|
*c++ = *p;
|
|
len++;
|
|
nv = 0;
|
|
continue;
|
|
}
|
|
|
|
if (vtype) {
|
|
char *s = p, *e, *vname, *vval = NULL;
|
|
size_t nlen;
|
|
|
|
s++;
|
|
|
|
if ((vtype == 1 || vtype == 3) && *s == '{') {
|
|
br = 1;
|
|
s++;
|
|
}
|
|
|
|
e = s;
|
|
vname = s;
|
|
while (*e) {
|
|
if (br == 1 && *e == '}') {
|
|
br = 0;
|
|
*e++ = '\0';
|
|
break;
|
|
}
|
|
|
|
if (br > 0) {
|
|
if (e != s && *e == '{') {
|
|
br++;
|
|
} else if (br > 1 && *e == '}') {
|
|
br--;
|
|
}
|
|
}
|
|
|
|
e++;
|
|
}
|
|
p = e > endof_indup ? endof_indup : e;
|
|
|
|
vval = NULL;
|
|
for(sb = vname; sb && *sb; sb++) {
|
|
if (*sb == ' ') {
|
|
vval = sb;
|
|
break;
|
|
} else if (*sb == '(') {
|
|
vval = sb;
|
|
br = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vval) {
|
|
e = vval - 1;
|
|
*vval++ = '\0';
|
|
|
|
while (*e == ' ') {
|
|
*e-- = '\0';
|
|
}
|
|
e = vval;
|
|
|
|
while (e && *e) {
|
|
if (*e == '(') {
|
|
br++;
|
|
} else if (br > 1 && *e == ')') {
|
|
br--;
|
|
} else if (br == 1 && *e == ')') {
|
|
*e = '\0';
|
|
break;
|
|
}
|
|
e++;
|
|
}
|
|
|
|
vtype = 2;
|
|
}
|
|
|
|
if (vtype == 1 || vtype == 3) {
|
|
char *expanded = NULL;
|
|
int offset = 0;
|
|
int ooffset = 0;
|
|
char *ptr;
|
|
int idx = -1;
|
|
|
|
if ((expanded = kz_event_expand_headers_check(event, (char *) vname, var_list, api_list, recur+1)) == vname) {
|
|
expanded = NULL;
|
|
} else {
|
|
vname = expanded;
|
|
}
|
|
if ((ptr = strchr(vname, ':'))) {
|
|
*ptr++ = '\0';
|
|
offset = atoi(ptr);
|
|
if ((ptr = strchr(ptr, ':'))) {
|
|
ptr++;
|
|
ooffset = atoi(ptr);
|
|
}
|
|
}
|
|
|
|
if ((ptr = strchr(vname, '[')) && strchr(ptr, ']')) {
|
|
*ptr++ = '\0';
|
|
idx = atoi(ptr);
|
|
}
|
|
|
|
if (vtype == 3 || !(sub_val = switch_event_get_header_idx(event, vname, idx))) {
|
|
switch_safe_free(gvar);
|
|
if ((gvar = switch_core_get_variable_dup(vname))) {
|
|
sub_val = gvar;
|
|
}
|
|
|
|
if (var_list && !switch_event_check_permission_list(var_list, vname)) {
|
|
sub_val = "<Variable Expansion Permission Denied>";
|
|
}
|
|
|
|
|
|
if ((expanded_sub_val = kz_event_expand_headers_check(event, sub_val, var_list, api_list, recur+1)) == sub_val) {
|
|
expanded_sub_val = NULL;
|
|
} else {
|
|
sub_val = expanded_sub_val;
|
|
}
|
|
}
|
|
|
|
if (sub_val) {
|
|
if (offset || ooffset) {
|
|
cloned_sub_val = strdup(sub_val);
|
|
switch_assert(cloned_sub_val);
|
|
sub_val = cloned_sub_val;
|
|
}
|
|
|
|
if (offset >= 0) {
|
|
sub_val += offset;
|
|
} else if ((size_t) abs(offset) <= strlen(sub_val)) {
|
|
sub_val = cloned_sub_val + (strlen(cloned_sub_val) + offset);
|
|
}
|
|
|
|
if (ooffset > 0 && (size_t) ooffset < strlen(sub_val)) {
|
|
if ((ptr = (char *) sub_val + ooffset)) {
|
|
*ptr = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
switch_safe_free(expanded);
|
|
} else {
|
|
switch_stream_handle_t stream = { 0 };
|
|
char *expanded = NULL;
|
|
char *expanded_vname = NULL;
|
|
|
|
SWITCH_STANDARD_STREAM(stream);
|
|
|
|
if ((expanded_vname = kz_event_expand_headers_check(event, (char *) vname, var_list, api_list, recur+1)) == vname) {
|
|
expanded_vname = NULL;
|
|
} else {
|
|
vname = expanded_vname;
|
|
}
|
|
|
|
if ((expanded = kz_event_expand_headers_check(event, vval, var_list, api_list, recur+1)) == vval) {
|
|
expanded = NULL;
|
|
} else {
|
|
vval = expanded;
|
|
}
|
|
|
|
if (!switch_core_test_flag(SCF_API_EXPANSION) || (api_list && !switch_event_check_permission_list(api_list, vname))) {
|
|
func_val = NULL;
|
|
sub_val = "<API execute Permission Denied>";
|
|
} else {
|
|
stream.param_event = event;
|
|
if (kz_expand_api_execute(vname, vval, NULL, &stream) == SWITCH_STATUS_SUCCESS) {
|
|
func_val = stream.data;
|
|
sub_val = func_val;
|
|
} else {
|
|
free(stream.data);
|
|
}
|
|
}
|
|
|
|
switch_safe_free(expanded);
|
|
switch_safe_free(expanded_vname);
|
|
}
|
|
if ((nlen = sub_val ? strlen(sub_val) : 0)) {
|
|
if (len + nlen >= olen) {
|
|
kz_resize(nlen);
|
|
}
|
|
|
|
len += nlen;
|
|
strcat(c, sub_val);
|
|
c += nlen;
|
|
}
|
|
|
|
switch_safe_free(func_val);
|
|
switch_safe_free(cloned_sub_val);
|
|
switch_safe_free(expanded_sub_val);
|
|
sub_val = NULL;
|
|
vname = NULL;
|
|
vtype = 0;
|
|
br = 0;
|
|
}
|
|
|
|
if (sp) {
|
|
if (len + 1 >= olen) {
|
|
kz_resize(1);
|
|
}
|
|
|
|
*c++ = ' ';
|
|
sp = 0;
|
|
len++;
|
|
}
|
|
|
|
if (*p == '$') {
|
|
p--;
|
|
} else {
|
|
if (len + 1 >= olen) {
|
|
kz_resize(1);
|
|
}
|
|
|
|
*c++ = *p;
|
|
len++;
|
|
}
|
|
}
|
|
}
|
|
free(indup);
|
|
switch_safe_free(gvar);
|
|
|
|
return data;
|
|
}
|
|
|
|
SWITCH_DECLARE(char *) kz_event_expand_headers(switch_event_t *event, const char *in)
|
|
{
|
|
return kz_event_expand_headers_check(event, in, NULL, NULL, 0);
|
|
}
|
|
|
|
SWITCH_DECLARE(char *) kz_event_expand_headers_pool(switch_memory_pool_t *pool, switch_event_t *event, char *val)
|
|
{
|
|
char *expanded;
|
|
char *dup = NULL;
|
|
|
|
expanded = kz_event_expand_headers(event, val);
|
|
dup = switch_core_strdup(pool, expanded);
|
|
|
|
if (expanded != val) {
|
|
free(expanded);
|
|
}
|
|
|
|
return dup;
|
|
}
|
|
|
|
SWITCH_DECLARE(char *) kz_event_expand(const char *in)
|
|
{
|
|
switch_event_t *event = NULL;
|
|
char *ret = NULL;
|
|
kz_switch_core_base_headers_for_expand(&event);
|
|
ret = kz_event_expand_headers_check(event, in, NULL, NULL, 0);
|
|
switch_event_destroy(&event);
|
|
return ret;
|
|
}
|
|
|
|
SWITCH_DECLARE(char *) kz_expand(const char *in)
|
|
{
|
|
switch_event_t *event = NULL;
|
|
char *ret = NULL;
|
|
kz_switch_core_base_headers_for_expand(&event);
|
|
ret = kz_event_expand_headers_check(event, in, NULL, NULL, 0);
|
|
switch_event_destroy(&event);
|
|
return ret;
|
|
}
|
|
|
|
SWITCH_DECLARE(char *) kz_expand_pool(switch_memory_pool_t *pool, const char *in)
|
|
{
|
|
char *expanded;
|
|
char *dup = NULL;
|
|
|
|
if(!(expanded = kz_expand(in))) {
|
|
return NULL;
|
|
}
|
|
dup = switch_core_strdup(pool, expanded);
|
|
|
|
if (expanded != in) {
|
|
switch_safe_free(expanded);
|
|
}
|
|
|
|
return dup;
|
|
}
|
|
|
|
char* kz_switch_event_get_first_of(switch_event_t *event, const char *list[])
|
|
{
|
|
switch_event_header_t *header = NULL;
|
|
int i = 0;
|
|
while(list[i] != NULL) {
|
|
if((header = switch_event_get_header_ptr(event, list[i])) != NULL)
|
|
break;
|
|
i++;
|
|
}
|
|
if(header != NULL) {
|
|
return header->value;
|
|
} else {
|
|
return "nodomain";
|
|
}
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) kz_switch_event_add_variable_name_printf(switch_event_t *event, switch_stack_t stack, const char *val, const char *fmt, ...)
|
|
{
|
|
int ret = 0;
|
|
char *varname;
|
|
va_list ap;
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
switch_assert(event != NULL);
|
|
|
|
|
|
va_start(ap, fmt);
|
|
ret = switch_vasprintf(&varname, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (ret == -1) {
|
|
return SWITCH_STATUS_MEMERR;
|
|
}
|
|
|
|
status = switch_event_add_header_string(event, stack, varname, val);
|
|
|
|
free(varname);
|
|
|
|
return status;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) kz_expand_json_to_event(cJSON *json, switch_event_t *event, char * prefix)
|
|
{
|
|
char * fmt = switch_mprintf("%s%s%%s", prefix ? prefix : "", prefix ? "_" : "");
|
|
if (event) {
|
|
cJSON *item = NULL;
|
|
char *response = NULL;
|
|
cJSON_ArrayForEach(item, json) {
|
|
if (item->type == cJSON_String) {
|
|
response = strdup(item->valuestring);
|
|
} else if (item->type == cJSON_Object) {
|
|
char * fmt1 = switch_mprintf(fmt, item->string);
|
|
kz_expand_json_to_event(item, event, fmt1);
|
|
switch_safe_free(fmt1);
|
|
continue;
|
|
} else {
|
|
response = cJSON_PrintUnformatted(item);
|
|
}
|
|
kz_switch_event_add_variable_name_printf(event, SWITCH_STACK_BOTTOM, response, fmt, item->string);
|
|
switch_safe_free(response);
|
|
}
|
|
}
|
|
|
|
switch_safe_free(fmt);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_xml_t) kz_xml_child(switch_xml_t xml, const char *name)
|
|
{
|
|
xml = (xml) ? xml->child : NULL;
|
|
while (xml && strcasecmp(name, xml->name))
|
|
xml = xml->sibling;
|
|
return xml;
|
|
}
|
|
|
|
void kz_xml_process(switch_xml_t cfg)
|
|
{
|
|
switch_xml_t xml_process;
|
|
for (xml_process = kz_xml_child(cfg, "X-PRE-PROCESS"); xml_process; xml_process = xml_process->next) {
|
|
const char *cmd = switch_xml_attr(xml_process, "cmd");
|
|
const char *data = switch_xml_attr(xml_process, "data");
|
|
if(cmd != NULL && !strcasecmp(cmd, "set") && data) {
|
|
char *name = (char *) data;
|
|
char *val = strchr(name, '=');
|
|
|
|
if (val) {
|
|
char *ve = val++;
|
|
while (*val && *val == ' ') {
|
|
val++;
|
|
}
|
|
*ve-- = '\0';
|
|
while (*ve && *ve == ' ') {
|
|
*ve-- = '\0';
|
|
}
|
|
}
|
|
|
|
if (name && val) {
|
|
switch_core_set_variable(name, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void kz_event_decode(switch_event_t *event)
|
|
{
|
|
switch_event_header_t *hp;
|
|
int i;
|
|
for (hp = event->headers; hp; hp = hp->next) {
|
|
if (hp->idx) {
|
|
for(i = 0; i < hp->idx; i++) {
|
|
switch_url_decode(hp->array[i]);
|
|
}
|
|
} else {
|
|
switch_url_decode(hp->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void kz_expand_headers(switch_event_t *resolver, switch_event_t *event) {
|
|
switch_event_t *clone = NULL;
|
|
switch_event_header_t *header = NULL;
|
|
switch_event_create_plain(&clone, event->event_id);
|
|
|
|
for(header = event->headers; header; header = header->next) {
|
|
char *expanded = kz_event_expand_headers(resolver, header->value);
|
|
if (expanded != header->value) {
|
|
switch_event_add_header_string(clone, SWITCH_STACK_BOTTOM, header->name, expanded);
|
|
switch_safe_free(expanded);
|
|
}
|
|
}
|
|
|
|
/* we don't want to force unique headers
|
|
* so we delete and then merge
|
|
*/
|
|
for(header = clone->headers; header; header = header->next) {
|
|
switch_event_del_header(event, header->name);
|
|
}
|
|
|
|
switch_event_merge(event, clone);
|
|
|
|
switch_event_destroy(&clone);
|
|
}
|
|
|
|
void kz_expand_headers_self(switch_event_t *event) {
|
|
kz_expand_headers(event, event);
|
|
}
|
|
|
|
char * kz_expand_vars(char *xml_str) {
|
|
return kz_expand_vars_pool(xml_str, NULL);
|
|
}
|
|
|
|
char * kz_expand_vars_pool(char *xml_str, switch_memory_pool_t *pool) {
|
|
char *var, *val;
|
|
char *rp = xml_str; /* read pointer */
|
|
char *ep, *wp, *buff; /* end pointer, write pointer, write buffer */
|
|
|
|
if (!(strstr(xml_str, "$${"))) {
|
|
return xml_str;
|
|
}
|
|
|
|
switch_zmalloc(buff, strlen(xml_str) * 2);
|
|
wp = buff;
|
|
ep = buff + (strlen(xml_str) * 2) - 1;
|
|
|
|
while (*rp && wp < ep) {
|
|
if (*rp == '$' && *(rp + 1) == '$' && *(rp + 2) == '{') {
|
|
char *e = switch_find_end_paren(rp + 2, '{', '}');
|
|
|
|
if (e) {
|
|
rp += 3;
|
|
var = rp;
|
|
*e++ = '\0';
|
|
rp = e;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "trying to expand %s \n", var);
|
|
if ((val = switch_core_get_variable_dup(var))) {
|
|
char *p;
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "expanded %s to %s\n", var, val);
|
|
for (p = val; p && *p && wp <= ep; p++) {
|
|
*wp++ = *p;
|
|
}
|
|
switch_safe_free(val);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
*wp++ = *rp++;
|
|
}
|
|
|
|
*wp++ = '\0';
|
|
|
|
if(pool) {
|
|
char * ret = switch_core_strdup(pool, buff);
|
|
switch_safe_free(buff);
|
|
return ret;
|
|
} else {
|
|
switch_safe_free(xml_str);
|
|
return buff;
|
|
}
|
|
|
|
}
|
|
|
|
switch_status_t kz_json_api(const char * command, cJSON *args, cJSON **res)
|
|
{
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
cJSON *req = cJSON_CreateObject();
|
|
cJSON_AddItemToObject(req, "command", cJSON_CreateString(command));
|
|
cJSON_AddItemToObject(req, "data", args ? args : cJSON_CreateObject());
|
|
status = switch_json_api_execute(req, NULL, res);
|
|
cJSON_Delete(req);
|
|
return status;
|
|
}
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
|
|
*/
|