1 /* Copyright (C) 2008, 2009 Vincent Penquerc'h.
2    This file is part of the Kate codec library.
3    Written by Vincent Penquerc'h.
4 
5    Use, distribution and reproduction of this library is governed
6    by a BSD style source license included with this source in the
7    file 'COPYING'. Please read these terms before distributing. */
8 
9 
10 #define KATE_INTERNAL
11 #include "kate_internal.h"
12 
13 #include <stdio.h>
14 #ifdef HAVE_STDLIB_H
15 #include <stdlib.h>
16 #endif
17 #ifdef HAVE_STRING_H
18 #include <string.h>
19 #endif
20 #include "kate/kate.h"
21 
22 /**
23   \ingroup comments
24   Initializes a kate_comment structure.
25   \param kc the structure to initialize
26   \returns 0 success
27   \returns KATE_E_* error
28   */
kate_comment_init(kate_comment * kc)29 int kate_comment_init(kate_comment *kc)
30 {
31   if (!kc) return KATE_E_INVALID_PARAMETER;
32 
33   kc->user_comments=NULL;
34   kc->comment_lengths=NULL;
35   kc->comments=0;
36   kc->vendor=NULL;
37 
38   return 0;
39 }
40 
41 /**
42   \ingroup comments
43   Destroys a kate_comment structure, it must be initialized again before being used.
44   \param kc the structure to clear
45   \returns 0 success
46   \returns KATE_E_* error
47   */
kate_comment_clear(kate_comment * kc)48 int kate_comment_clear(kate_comment *kc)
49 {
50   int n;
51 
52   if (!kc) return KATE_E_INVALID_PARAMETER;
53 
54   for (n=0;n<kc->comments;++n) kate_free(kc->user_comments[n]);
55   if (kc->user_comments) kate_free(kc->user_comments);
56   if (kc->comment_lengths) kate_free(kc->comment_lengths);
57   if (kc->vendor) kate_free(kc->vendor);
58 
59   return 0;
60 }
61 
kate_comment_check_tag(const char * tag,size_t len)62 static int kate_comment_check_tag(const char *tag,size_t len)
63 {
64   if (!tag) return KATE_E_INVALID_PARAMETER;
65 
66   if (len==0) return KATE_E_BAD_TAG;
67   while (len--) {
68     int c=*tag++;
69     if (c<0x20 || c>0x7d || c=='=') return KATE_E_BAD_TAG;
70   }
71   return 0;
72 }
73 
74 /**
75   \ingroup comments
76   Adds a comment to the kate_comment structure.
77   The comment must be of the form "tag=value"
78   The comment tag must be 7 bit ASCII, and may not contain embedded NULs
79   The comment value is UTF-8 and may contain embedded NULs
80   \param kc the kate_comment structure to add the comment to
81   \param comment the comment to add (a stream of len bytes)
82   \param len the number of bytes in the comment
83   \returns 0 success
84   \returns KATE_E_* error
85   */
kate_comment_add_length(kate_comment * kc,const char * comment,size_t len)86 int kate_comment_add_length(kate_comment *kc,const char *comment,size_t len)
87 {
88   int *cl;
89   char **uc;
90   const char *equals;
91   int ret;
92 
93   if (!kc || !comment) return KATE_E_INVALID_PARAMETER;
94 
95   ret=kate_check_add_overflow(kc->comments,1,NULL);
96   if (ret<0) return ret;
97   ret=kate_check_add_overflow(len,1,NULL);
98   if (ret<0) return ret;
99 
100   equals=memchr(comment,'=',len);
101   if (!equals) return KATE_E_BAD_TAG;
102   ret=kate_comment_check_tag(comment,equals-comment);
103   if (ret<0) return ret;
104   ret=kate_text_validate(kate_utf8,equals,len-(equals-comment));
105   if (ret<0) return ret;
106 
107   uc=kate_checked_realloc(kc->user_comments,(kc->comments+1),sizeof(char*));
108   if (!uc) return KATE_E_OUT_OF_MEMORY;
109   kc->user_comments=uc;
110   cl=kate_checked_realloc(kc->comment_lengths,(kc->comments+1),sizeof(int));
111   if (!cl) return KATE_E_OUT_OF_MEMORY;
112   kc->comment_lengths=cl;
113 
114   kc->user_comments[kc->comments]=(char*)kate_malloc(len+1);
115   if (!kc->user_comments[kc->comments]) return KATE_E_OUT_OF_MEMORY;
116   memcpy(kc->user_comments[kc->comments],comment,len);
117   kc->user_comments[kc->comments][len]=0;
118   kc->comment_lengths[kc->comments]=len;
119 
120   ++kc->comments;
121 
122   return 0;
123 }
124 
125 /**
126   \ingroup comments
127   Adds a comment to the kate_comment structure.
128   The comment must be of the form "tag=value"
129   The comment tag must be 7 bit ASCII, and may not contain embedded NULs
130   The comment value is UTF-8 and may not contain embedded NULs as the comments ends at the first NUL encountered
131   \param kc the kate_comment structure to add the comment to
132   \param comment the comment to add, NUL terminated
133   \returns 0 success
134   \returns KATE_E_* error
135   */
kate_comment_add(kate_comment * kc,const char * comment)136 int kate_comment_add(kate_comment *kc,const char *comment)
137 {
138   if (!kc || !comment) return KATE_E_INVALID_PARAMETER;
139 
140   return kate_comment_add_length(kc,comment,strlen(comment));
141 }
142 
143 /**
144   \ingroup comments
145   Adds a comment to the kate_comment structure, formatted as "tag=value".
146   The tag must be 7 bit ASCII and comply with Vorbis comment tag rules.
147   The value must be valid UTF-8 text.
148   Neither tag nor value may contain embedded NULs. To embed comments with
149   embedded NUL in the payload, see kate_comment_add_length.
150   \param kc the kate_comment structure to add the comment to
151   \param tag the tag of the comment to add
152   \param value the contents of the comment to add
153   \returns 0 success
154   \returns KATE_E_* error
155   */
kate_comment_add_tag(kate_comment * kc,const char * tag,const char * value)156 int kate_comment_add_tag(kate_comment *kc,const char *tag,const char *value)
157 {
158   char *comment;
159 
160   if (!kc || !tag || !value) return KATE_E_INVALID_PARAMETER;
161 
162   comment=(char*)kate_malloc(strlen(tag)+1+strlen(value)+1);
163   if (!comment) return KATE_E_OUT_OF_MEMORY;
164   sprintf(comment,"%s=%s",tag,value);
165   kate_comment_add(kc,comment);
166   kate_free(comment);
167 
168   return 0;
169 }
170 
171 /**
172   \ingroup comments
173   Queries the value of a comment that has the given tag.
174   The tags are case insensitive, so "tag", "TAG", "Tag", and "TaG" are all equivalent.
175   If there are multiple comments with the same tag, count may be used to
176   select which one to return.
177   The number of comments with a given tag may be retrieved using kate_comment_query_count.
178   \param kc the kate_comment structure to look into
179   \param tag the title of the comment to look for
180   \param count the index of the matching comment to return (if there are several)
181   \returns 0 success
182   \returns KATE_E_* error
183   */
kate_comment_query(const kate_comment * kc,const char * tag,int count)184 const char *kate_comment_query(const kate_comment *kc,const char *tag,int count)
185 {
186   int n;
187   size_t bytes;
188 
189   if (!kc) return NULL;
190 
191   for (n=0;n<kc->comments;++n) {
192     const char *eq=strchr(kc->user_comments[n],'=');
193     if (!eq) continue; /* wrong format */
194     bytes=eq-kc->user_comments[n];
195     if (!kate_ascii_strncasecmp(tag,kc->user_comments[n],bytes)) {
196       if (count==0) return eq+1;
197       --count;
198     }
199   }
200   /* not found */
201   return NULL;
202 }
203 
204 /**
205   \ingroup comments
206   Returns the number of comments with the given tag.
207   The tags are case insensitive, so "tag", "TAG", "Tag", and "TaG" are all equivalent.
208   \param kc the kate_comment structure to look into
209   \param tag the title of the comment to look for
210   \returns 0 success
211   \returns KATE_E_* error
212   */
kate_comment_query_count(const kate_comment * kc,const char * tag)213 int kate_comment_query_count(const kate_comment *kc,const char *tag)
214 {
215   int n,count;
216   size_t bytes;
217 
218   if (!kc) return KATE_E_INVALID_PARAMETER;
219 
220   count=0;
221   for (n=0;n<kc->comments;++n) {
222     const char *eq=strchr(kc->user_comments[n],'=');
223     if (!eq) continue; /* wrong format */
224     bytes=eq-kc->user_comments[n];
225     if (!kate_ascii_strncasecmp(tag,kc->user_comments[n],bytes)) {
226       ++count;
227     }
228   }
229 
230   return count;
231 }
232 
233