/* * Miranda-IM Vypress Chat/quickChat plugins * Copyright (C) Saulius Menkevicius * * This program is free software; you can redistribute it and/or modify * 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. * * This program is distributed in the hope that it will be useful, * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: chanlist.c,v 1.10 2005/03/07 14:26:54 bobas Exp $ */ #include "miranda.h" #include "main.h" #include "chanlist.h" /* internal routines */ /* chanlist_find_channel: * Returns a pointer to channel in a channel list * (or a NULL, if not found) * *p_cl_len will be set to the length of channel list. * *p_ch_len will be set to the length of a channel without the '#'. */ static const char * chanlist_find_channel( const char * chanlist, const char * channel, int * p_cl_len, int * p_ch_len) { char * cl_channel; int cl_len, ch_len; if(!chanlist) return NULL; /* get chanlist and channel lengths */ cl_len = strlen(chanlist); ASSERT_RETURNVALIFFAIL(cl_len >= 2 && chanlist[0]=='#', NULL); if(p_cl_len) *p_cl_len = cl_len; ch_len = strlen(channel); ASSERT_RETURNVALIFFAIL(ch_len!=0, NULL); if(p_ch_len) *p_ch_len = ch_len; /* the list doesn't contain a channel if it's smaller than * the channel itself */ if(cl_len-1 < ch_len) return NULL; /* find the channel in the list */ cl_channel = strstr(chanlist, channel); /* there must be a '#' before channel name */ if(cl_channel==NULL || *(cl_channel - 1) != '#') return NULL; /* and it must be last on the channel list, or end up with '#' */ if(cl_channel[ch_len]!='\0' && cl_channel[ch_len]!='#') return NULL; /* ok, the channel was found */ return cl_channel; } /* exported routines */ /* chanlist_is_valid: * returns if the chanlist is in valid format */ int chanlist_is_valid(const char * chanlist, int no_empty_channels) { int cl_len; /* chanlist can be empty -- NULL */ if(chanlist==NULL) return 1; /* non-empty chanlist must be with length >= 0, * and must begin with a '#' */ cl_len = strlen(chanlist); if(cl_len < 2 || chanlist[0]!='#') return 0; if(no_empty_channels) { /* check that there are no 0-length channel names * in the list */ const char * prev = chanlist, * next; do { next = strchr(prev + 1, '#'); if(next && (next - prev) == 1) return 0; prev = next; } while(next); } return 1; /* this chanlist is supposedly valid */ } /* chanlist_add: * Creates a channel list (if chanlist==NULL) with a new channel * or add a new one to the end. * Checks if the channel is already in the list. * The result channel list string should look like: * "#channel1#channel2...#channelN". * An empty channel list is a (char*)NULL. */ char * chanlist_add(char * chanlist, const char * channel) { char * new_chanlist; int cl_len, ch_len; ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), chanlist); if(chanlist_find_channel(chanlist, channel, NULL, NULL)) return chanlist; /* the chanlist doesn't contain the new channel: * append it to the end */ ch_len = strlen(channel); ASSERT_RETURNVALIFFAIL(ch_len!=0, chanlist); if(chanlist) { /* get the length of channel list, note that an empty chanlist * MUST be (char*)NULL, and an non-empty chanlist must be at * least 2 chars in length ("#a") and have '#' at the beginning */ cl_len = strlen(chanlist); ASSERT_RETURNVALIFFAIL( cl_len >= 2 && chanlist[0]=='#', chanlist); } else { cl_len = 0; } /* allocate space for a previous channel list, plus a # character * and new channel, and a terminator */ new_chanlist = malloc((cl_len + ch_len + 1 + 1) * sizeof(char)); ASSERT_RETURNVALIFFAIL(new_chanlist!=NULL, chanlist); if(chanlist) { /* strcpy(new_chanlist, chanlist); */ memcpy(new_chanlist, chanlist, cl_len); free(chanlist); } new_chanlist[cl_len] = '#'; /* strcat(new_chanlist, "#"); */ /* strcat(new_chanlist, channel); */ memcpy(new_chanlist + cl_len + 1, channel, ch_len + 1); return new_chanlist; } /* chanlist_remove: * Removes a channel from chanlist and frees the resulting * chanlist, if it becomes empty (thus returning the (char*)NULL) */ char * chanlist_remove(char * chanlist, const char * channel) { char * cl_channel; int cl_len, ch_len; ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), chanlist); if(chanlist==NULL) return NULL; cl_channel = (char*)chanlist_find_channel( chanlist, channel, &cl_len, &ch_len); if(cl_channel == NULL) return chanlist; /* check if we need to free the list, (if it was the only channel) */ if(cl_len == ch_len + 1) { free(chanlist); return NULL; } /* if the channel was the last on the list, we put a terminator '\0' * and we're finished */ if((cl_channel - chanlist) + ch_len == cl_len) { *(cl_channel - 1) = '\0'; /* put '\0' on channel's '#' char */ return chanlist; } /* we need to move channels after cl_channel in the chanlist * to the place of cl_channel (including the '\0' terminator) */ memcpy(cl_channel, cl_channel + ch_len + 1, cl_len - (cl_channel - chanlist) - ch_len); return chanlist; } /* chanlist_shift: * removes the first channel from the list and returns * the chanlist without the channel * * params: * @p_chanlist - chanlist to shift the channel off * returns: * NULL, if the chanlist is empty * malloc'ed channel name shifted from the chanlist */ char * chanlist_shift(char ** p_chanlist) { char * next, * new_chanlist, * channel; /* check that the channel is valid */ ASSERT_RETURNVALIFFAIL(VALIDPTR(p_chanlist), NULL); if(!chanlist_is_valid(*p_chanlist, 0)) return NULL; /* check if chanlist is empty */ if(*p_chanlist==NULL) return NULL; /* get pointer to the next channel in the list */ next = strchr(*p_chanlist + 1, '#'); /* make a copy of the rest as new chanlist */ new_chanlist = chanlist_copy(next); /* finish channel name with a '\0' */ if(next) *next = '\0'; channel = *p_chanlist; memmove(channel, channel + 1, strlen(channel)); *p_chanlist = new_chanlist; return channel; } /* chanlist_merge: * Merges two chanlists, the result adds to chanlist_dst * and it is what returns. * * Note that chanlist_src might get modified in the process * thus it is non-const, but it is kept unmodified after return. */ static int chanlist_merge_enum_fn(const char * channel, void * enum_data) { char ** p_dst_chanlist = (char **)enum_data; ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), 0); ASSERT_RETURNVALIFFAIL(VALIDPTR(p_dst_chanlist), 0); *p_dst_chanlist = chanlist_add(*p_dst_chanlist, channel); return 1; /* keep enumerating */ } char * chanlist_merge(char * chanlist_dst, const char * chanlist_src) { chanlist_enum(chanlist_src, chanlist_merge_enum_fn, &chanlist_dst); return chanlist_dst; } /* chanlist_contains: * Returns non-0, if the chanlist contains the specified channel. */ int chanlist_contains(const char * chanlist, const char * channel) { ASSERT_RETURNVALIFFAIL(VALIDPTR(channel), 0); return chanlist_find_channel(chanlist, channel, NULL, NULL) != NULL; } /* chanlist_enum: * Enumerates chanlist. * The enum function should return non-0 to keep enumeration going, * or 0 to stop enumeration. */ void chanlist_enum( const char * orig_chanlist, chanlist_enum_fn enum_fn, void * enum_data) { char * cl_channel, * cl_next_block; int cl_len; char * chanlist = chanlist_copy(orig_chanlist); ASSERT_RETURNIFFAIL(enum_fn!=NULL); /* do no enumeration if chanlist is empty */ if(chanlist == NULL) return; /* get chanlist length */ cl_len = strlen(chanlist); /* the length must be at least 2 chars ("#a"), and begin with a '#' */ ASSERT_RETURNIFFAIL(cl_len >= 2 && chanlist[0]=='#'); /* ok, walk the channel list.. */ cl_channel = chanlist + 1; do { /* get address of the next channel's '#' character */ cl_next_block = strchr(cl_channel, '#'); if(cl_next_block) { /* temporary add a terminator and invoke the callback */ *cl_next_block = '\0'; enum_fn(cl_channel, enum_data); /* remove the terminator */ *cl_next_block = '#'; } else { /* the channel is the last on the list: invoke cb */ enum_fn(cl_channel, enum_data); } /* skip to next channel */ if(cl_next_block) cl_channel = cl_next_block + 1; else cl_channel = NULL; } while(cl_channel); /* free the chanlist copy */ chanlist_free(chanlist); } /* chanlist_make_vqp_chanlist: * allocates a vqp chanlist (the same as chanlist, only with '#' at the end) * and always non-NULL */ char * chanlist_make_vqp_chanlist(const char * chanlist) { int cl_len; char * vqp_chanlist; cl_len = chanlist ? strlen(chanlist): 0; vqp_chanlist = malloc(cl_len + 2); ASSERT_RETURNVALIFFAIL(VALIDPTR(vqp_chanlist), NULL); if(chanlist) memcpy(vqp_chanlist, chanlist, cl_len); /* append the '#' and '\0' terminator at the end */ vqp_chanlist[cl_len] = '#'; vqp_chanlist[cl_len + 1] = '\0'; return vqp_chanlist; } /* chanlist_parse_vqp_chanlist: * makes a chanlist from vqp chanlist format */ char * chanlist_parse_vqp_chanlist(const char * vqp_chanlist) { int vqp_cl_len; char * chanlist; ASSERT_RETURNVALIFFAIL(VALIDPTR(vqp_chanlist), NULL); vqp_cl_len = strlen(vqp_chanlist); ASSERT_RETURNVALIFFAIL(vqp_cl_len != 0, NULL); /* vqp_chanlist must begin and end with '#' */ ASSERT_RETURNVALIFFAIL( vqp_chanlist[0]=='#' && vqp_chanlist[vqp_cl_len-1]=='#', NULL); /* make the chanlist (copy everything, except the last '#') */ chanlist = malloc(vqp_cl_len); memcpy(chanlist, vqp_chanlist, vqp_cl_len - 1); chanlist[vqp_cl_len - 1] ='\0'; return chanlist; }