00001 /*! \file replacer.c 00002 This file contains the implementation of the Search / Replace API. 00003 */ 00004 00005 #include <stdio.h> 00006 #include <stdlib.h> 00007 #include <string.h> 00008 #include "replacer.h" 00009 #include "dstring.h" 00010 #include "chtbl.h" 00011 00012 /*! \brief Array of strings. This variable is defined in the parser's module (see file 'replacer.lex'). 00013 It will contain all the tokens extracted by the parser. 00014 */ 00015 00016 extern char** txt_tokens; 00017 00018 /*! \brief Number of tokens in the array 'txt_tokens'. This variable is defined in the parser's module 00019 (see file 'replacer.lex'). 00020 */ 00021 00022 extern size_t nb_tokens; 00023 00024 /*! \brief Initial size for a dynamic string. 00025 */ 00026 00027 #define INIT_DYNAMIC_STRING 100 00028 00029 /* ----------------------------------------------------- */ 00030 /* Hash table management */ 00031 /* ----------------------------------------------------- */ 00032 00033 /*! \brief Type of the elements strored into the hash table. 00034 */ 00035 00036 struct couple { 00037 00038 /*! \brief This point points to a zero terminated string of characters that represent a 00039 tag. */ 00040 00041 char *key; 00042 00043 /*! \brief This point points to a zero terminated string of characters that represent the 00044 value of a tag. */ 00045 00046 char *value; 00047 }; 00048 00049 /*! \brief Number of linked lists for the hash tables used to store the couples (tags, values). 00050 */ 00051 00052 #define HASH_TABLE_ENTRIES 150 00053 00054 /*! \brief Hash function used by the hash table to hash the tags. 00055 \param key Pointer to a structure <i>couple</i> that contains the couple (tag, value). 00056 \return The function returns an integer. 00057 \warning Make sure that the structure pointed by <i>key</i> has been initialized! 00058 */ 00059 00060 static int hash_function (const void *key) 00061 { 00062 struct couple *data; 00063 int total, i; 00064 00065 data = (struct couple*)key; 00066 total = 1; 00067 00068 for (i=0; i<strlen(data->key); i++) { total *= (int)((data->key)[i]) ; } 00069 00070 if (total < 0) { total *= -1; } 00071 00072 return total; 00073 } 00074 00075 /*! \brief Function used to free memory allocated for the elements of the hash table. 00076 \param d Pointer to a structure <i>couple</i> to be freed. 00077 */ 00078 00079 static void free_user_data (void *d) 00080 { 00081 struct couple *data; 00082 00083 data = (struct couple*)d; 00084 free (data); 00085 return; 00086 } 00087 00088 /*! \brief Function used to compare two elements of the hash table (two tags). 00089 \param d1 First element to compare. 00090 \param d2 Second element to compare. 00091 \return The function return 1 if d1 == d2, 0 otherwise. 00092 */ 00093 00094 static int compare_user_data (const void *d1, const void *d2) 00095 { 00096 struct couple *data1, *data2; 00097 00098 data1 = (struct couple*)d1; 00099 data2 = (struct couple*)d2; 00100 00101 return !strcmp(data1->key, data2->key); 00102 } 00103 00104 /* ----------------------------------------------------- */ 00105 /* End of hash table management */ 00106 /* ----------------------------------------------------- */ 00107 00108 /*! \brief Replace tags by their values in a text. 00109 \param text Pointer to a zero terminated string of characters. This string contains the original text. 00110 \param tags Pointer to an array of tags (that will be searched in 'text'). 00111 \param values Pointer to an array of values (a tag is replaced by a value). Please note that: 00112 'tags[i]' will be replaced by 'values[i]'. 00113 \param nb_tags Number of elements in the arrays 'text' and 'tags'. 00114 \return The function returns a pointer to a zero terminated string of characters that represents the 00115 final text. If the function could not allocate memory, then the returned value is NULL. 00116 \warning <ul> 00117 <li>The returned string of characters has been allocated within the function. Therefore you must 00118 free it yourself (using free()). 00119 <li>This function is <b>NOT REENTRANT</b>, DO NOT use it with threads! 00120 <li>This function works in memory, so if the input text is too large, the function may fail 00121 because the process is running out of memory. In this case the function returns the value 00122 NULL. If you get NULL, you should abort the process execution, because the function does 00123 not clean the memory allocated... 00124 </ul> 00125 */ 00126 00127 char* replace_tags (char *text, char **tags, char **values, int nb_tags) 00128 { 00129 int res, i; 00130 char *final, *pt, *aux; 00131 dstring ds; 00132 size_t s; 00133 CHTbl htab; 00134 struct couple *elem, selem; 00135 00136 /* --------------------------------------------------- */ 00137 /* Initialize the hash table */ 00138 /* --------------------------------------------------- */ 00139 00140 if ( 00141 chtbl_init ( 00142 &htab, 00143 HASH_TABLE_ENTRIES, 00144 hash_function, 00145 compare_user_data, 00146 free_user_data 00147 ) == -1 00148 ) 00149 { return NULL; } 00150 00151 /* --------------------------------------------------- */ 00152 /* Load hash table with couples (tags => values) */ 00153 /* --------------------------------------------------- */ 00154 00155 for (i=0; i<nb_tags; i++) 00156 { 00157 elem = (struct couple*)malloc(sizeof(struct couple)); 00158 if (elem == NULL) 00159 { 00160 chtbl_destroy (&htab); 00161 return NULL; 00162 } 00163 00164 elem->key = tags[i]; 00165 elem->value = values[i]; 00166 00167 res = chtbl_insert (&htab, (void*)elem); 00168 00169 switch (res) 00170 { 00171 case -1: { 00172 chtbl_destroy (&htab); 00173 return NULL; 00174 }; break; 00175 00176 case 1: /* tag already in hash table */ break; 00177 } 00178 } 00179 00180 /* --------------------------------------------------- */ 00181 /* Parse the original text. */ 00182 /* This operation will fill the array 'txt_tokens' */ 00183 /* all the tokens. */ 00184 /* */ 00185 /* o Tags begins with a character '+'. */ 00186 /* o Other values begins with a character '-'. */ 00187 /* --------------------------------------------------- */ 00188 00189 res = yylex_memory(text); 00190 00191 if (res == RELACER_MEM_ERROR) 00192 { 00193 chtbl_destroy (&htab); 00194 return NULL; 00195 } 00196 00197 /* --------------------------------------------------- */ 00198 /* Initialize the dynamic string */ 00199 /* --------------------------------------------------- */ 00200 00201 if (dstring_init (&ds, INIT_DYNAMIC_STRING) == 1) 00202 { 00203 chtbl_destroy (&htab); 00204 return NULL; 00205 } 00206 00207 /* --------------------------------------------------- */ 00208 /* Look at each token and replace it if necessary. */ 00209 /* --------------------------------------------------- */ 00210 00211 for (i=0; i<nb_tokens; i++) 00212 { 00213 pt = txt_tokens[i]+1; 00214 00215 if ( *(txt_tokens[i]) == '-') 00216 { 00217 /* ---------------------------------------------- */ 00218 /* '-' means that this is NOT a tag, we copy it */ 00219 /* "as is". */ 00220 /* ---------------------------------------------- */ 00221 00222 if (dstring_add (&ds, pt, strlen(pt)) == 1) 00223 { 00224 chtbl_destroy (&htab); 00225 return NULL; 00226 } 00227 } 00228 else 00229 { 00230 /* ---------------------------------------------- */ 00231 /* This is a tag, lets replace it */ 00232 /* ---------------------------------------------- */ 00233 00234 selem.key = pt; 00235 selem.value = NULL; 00236 elem = &selem; 00237 00238 res = chtbl_lookup (&htab, (void**)&elem); 00239 00240 switch (res) 00241 { 00242 case 0: { 00243 /* element found */ 00244 00245 if (dstring_add (&ds, elem->value, strlen(elem->value)) == 1) 00246 { 00247 chtbl_destroy (&htab); 00248 return NULL; 00249 } 00250 00251 }; break; 00252 00253 default: { 00254 /* element not found */ 00255 00256 /* ######################################################## */ 00257 /* ### ### */ 00258 /* ### WARNING !!!! ### */ 00259 /* ### ### */ 00260 /* ### The following code depends on the tag's defini- ### */ 00261 /* ### -tion (see file 'replacer.lex'. ### */ 00262 /* ### ### */ 00263 /* ######################################################## */ 00264 00265 aux = (char*)malloc(strlen(pt)+4); 00266 if (aux == NULL) 00267 { 00268 dstring_free(&ds); 00269 chtbl_destroy (&htab); 00270 return NULL; 00271 } 00272 00273 sprintf (aux, "${%s}", pt); 00274 00275 if (dstring_add (&ds, aux, strlen(aux)) == 1) 00276 { 00277 free (aux); 00278 chtbl_destroy (&htab); 00279 return NULL; 00280 } 00281 00282 free (aux); 00283 00284 }; break; 00285 } 00286 } 00287 } 00288 00289 /* --------------------------------------------------- */ 00290 /* Free hash table */ 00291 /* --------------------------------------------------- */ 00292 00293 chtbl_destroy (&htab); 00294 00295 /* --------------------------------------------------- */ 00296 /* Free all allocated tokens. */ 00297 /* --------------------------------------------------- */ 00298 00299 free_tokens(); 00300 00301 /* --------------------------------------------------- */ 00302 /* Copy the content of the dynamic string */ 00303 /* --------------------------------------------------- */ 00304 00305 final = dstring_get_data (&ds, &s); 00306 if (final == NULL) { return NULL; } 00307 00308 /* --------------------------------------------------- */ 00309 /* Free the dynamic string */ 00310 /* --------------------------------------------------- */ 00311 00312 dstring_free(&ds); 00313 00314 return final; 00315 } 00316 00317 /*! \example test_replacer_final.c 00318 This file shows how to use the function replace_tags(). 00319 */