|
1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
|
24 * Use is subject to license terms. |
|
25 */ |
|
26 #ifdef HAVE_CONFIG_H |
|
27 #include <config.h> |
|
28 #endif |
|
29 |
|
30 #include <glib.h> |
|
31 #include <stdio.h> |
|
32 #include <stdlib.h> |
|
33 #include <string.h> |
|
34 #include <strings.h> |
|
35 #include <time.h> |
|
36 #include <sys/stat.h> |
|
37 #include <sys/types.h> |
|
38 #include <errno.h> |
|
39 |
|
40 #include "fsexam-debug.h" |
|
41 #include "encoding.h" |
|
42 #include "fsexam-history.h" |
|
43 #include "fsexam-header.h" |
|
44 |
|
45 /* |
|
46 * Print one Hist_item data structure |
|
47 */ |
|
48 static void |
|
49 print_hist_item (gpointer data, gpointer user_data) |
|
50 { |
|
51 Hist_item *item = (Hist_item *)data; |
|
52 |
|
53 if (item == NULL) |
|
54 return; |
|
55 |
|
56 printf ("%d:\t%d -> %d: %s\n", item->serial, item->from_encoding, |
|
57 item->to_encoding, |
|
58 g_filename_from_uri (item->uri, NULL, NULL)); |
|
59 |
|
60 return; |
|
61 } |
|
62 |
|
63 /* |
|
64 * Print the whole history info |
|
65 */ |
|
66 void |
|
67 print_history (Hist_info *info) |
|
68 { |
|
69 gint current; |
|
70 gint head; |
|
71 |
|
72 if (NULL == info) |
|
73 return; |
|
74 |
|
75 head = info->head; |
|
76 current = info->current; |
|
77 if (current == -1) { |
|
78 g_print ("History list is empty\n"); |
|
79 return; |
|
80 } |
|
81 |
|
82 g_print ("History_Info: head = %d, current = %d, max_len = %d," |
|
83 "last_serial = %d\n", |
|
84 current, head, |
|
85 info->max_len, info->last_serial); |
|
86 |
|
87 current = (current + 1) % info->max_len; |
|
88 head = (head + 1) % info->max_len; |
|
89 |
|
90 while (head != current) { |
|
91 GList *tmp = info->hist_array[head]; |
|
92 g_print ("------------------------------------------\n"); |
|
93 g_list_foreach (tmp, print_hist_item, NULL); |
|
94 |
|
95 head = (head + 1) % info->max_len; |
|
96 } |
|
97 |
|
98 return; |
|
99 } |
|
100 |
|
101 static void |
|
102 release_each_item (gpointer data_ptr, gpointer p) |
|
103 { |
|
104 Hist_item *h = (Hist_item *) data_ptr; |
|
105 |
|
106 if (h == NULL) |
|
107 return; |
|
108 |
|
109 g_free (h->uri); |
|
110 g_free (h); |
|
111 |
|
112 return; |
|
113 } |
|
114 |
|
115 /* |
|
116 * free the memory for Hist_item array list |
|
117 */ |
|
118 static void |
|
119 release_hist_array (Hist_info *info) |
|
120 { |
|
121 gint head, current; |
|
122 |
|
123 if (NULL == info) |
|
124 return; |
|
125 |
|
126 head = info->head; |
|
127 current = info->current; |
|
128 |
|
129 if (current == -1) |
|
130 return; |
|
131 |
|
132 while (current != head) { |
|
133 g_list_foreach (info->hist_array[current], release_each_item, NULL); |
|
134 g_list_free (info->hist_array[current]); |
|
135 |
|
136 current = (current + info->max_len - 1) % info->max_len; |
|
137 } |
|
138 |
|
139 /* restore to orignal state */ |
|
140 info->head = 0; |
|
141 info->current = -1; |
|
142 info->last_serial = 0; |
|
143 |
|
144 return; |
|
145 } |
|
146 |
|
147 /* |
|
148 * when current catch up head, need free some of space, currently hardcode |
|
149 * free 1/3 of space. |
|
150 */ |
|
151 static void |
|
152 reclaim_hist_elements (Hist_info *info) |
|
153 { |
|
154 gint newhead, current; |
|
155 |
|
156 if (NULL == info) |
|
157 return; |
|
158 |
|
159 newhead = (info->head + info->max_len / 3) % info->max_len; |
|
160 current = newhead; |
|
161 |
|
162 while (current != info->head) { |
|
163 g_list_foreach (info->hist_array[current], release_each_item, NULL); |
|
164 g_list_free (info->hist_array[current]); |
|
165 info->hist_array[current] = NULL; |
|
166 |
|
167 current = (current + info->max_len - 1) % info->max_len; |
|
168 } |
|
169 |
|
170 info->head = newhead; |
|
171 |
|
172 return; |
|
173 } |
|
174 |
|
175 /* |
|
176 * Store one Hist_item into history stream |
|
177 */ |
|
178 static void |
|
179 serialize_history_item (gpointer data_ptr, gpointer fp) |
|
180 { |
|
181 FILE *stream = (FILE *) fp; |
|
182 Hist_item *h = (Hist_item *) data_ptr; |
|
183 |
|
184 if (VALID_CONVTYPE (h->convtype)) { |
|
185 fprintf (stream, "%ud\27%d\27%d\27%d\27%s\n", |
|
186 h->serial, |
|
187 h->convtype, |
|
188 h->from_encoding, |
|
189 h->to_encoding, |
|
190 h->uri); |
|
191 } |
|
192 |
|
193 return; |
|
194 } |
|
195 |
|
196 /* Initialize in-memory structure from disk file */ |
|
197 static void |
|
198 deserialize_history_data (Hist_info *hist_info) |
|
199 { |
|
200 Hist_item *item = NULL; |
|
201 gchar linebuf[4096]; |
|
202 gint32 prev_serial = -1; |
|
203 gint32 current = -1; |
|
204 gboolean first = TRUE; |
|
205 |
|
206 if ((NULL == hist_info) || (NULL == hist_info->fp)) { |
|
207 g_print ("deserialize_history_data: file pointer is NULL\n"); |
|
208 return; |
|
209 } |
|
210 |
|
211 memset (linebuf, 0, sizeof(linebuf)); |
|
212 |
|
213 while ( fgets (linebuf, sizeof(linebuf), hist_info->fp) ) |
|
214 { |
|
215 gchar *serial = NULL; |
|
216 gchar *conv = NULL; |
|
217 gchar *from_encoding = NULL; |
|
218 gchar *to_encoding = NULL; |
|
219 gchar *uri = NULL; |
|
220 gchar *last_char = NULL; |
|
221 |
|
222 if ( linebuf [sizeof(linebuf)-1] != '\0' && |
|
223 linebuf [sizeof(linebuf)-1] != '\n') |
|
224 { |
|
225 memset (linebuf, 0, sizeof(linebuf)); |
|
226 continue; /* overflow */ |
|
227 } |
|
228 |
|
229 if (linebuf[strlen (linebuf) - 1] == '\n') |
|
230 linebuf [strlen(linebuf)-1] = '\0'; |
|
231 |
|
232 serial = strtok_r (linebuf, "\27", &last_char); |
|
233 conv = strtok_r (NULL, "\27", &last_char); |
|
234 from_encoding = strtok_r (NULL, "\27", &last_char); |
|
235 to_encoding = strtok_r (NULL, "\27", &last_char); |
|
236 uri = last_char; |
|
237 |
|
238 if (!serial || !conv || !from_encoding || !to_encoding || !uri) |
|
239 continue; |
|
240 |
|
241 item = g_new0 (Hist_item, 1); |
|
242 |
|
243 item->serial = atoi (serial); |
|
244 item->convtype = atoi (conv); |
|
245 item->from_encoding = atoi (from_encoding); |
|
246 item->to_encoding = atoi (to_encoding); |
|
247 item->uri = g_strdup (uri); |
|
248 |
|
249 if ((first) || (item->serial != prev_serial)) { |
|
250 if (first) { |
|
251 first = FALSE; |
|
252 current = 1; |
|
253 }else{ |
|
254 current = (current + 1) % hist_info->max_len; |
|
255 |
|
256 /* current catch up head, free space */ |
|
257 if (current == hist_info->head) { |
|
258 reclaim_hist_elements (hist_info); |
|
259 } |
|
260 } |
|
261 |
|
262 prev_serial = item->serial; |
|
263 |
|
264 hist_info->hist_array[current] = g_list_append ( |
|
265 hist_info->hist_array[current], |
|
266 item); |
|
267 }else{ /* The same pass of action, won't increment current */ |
|
268 hist_info->hist_array[current] = g_list_append ( |
|
269 hist_info->hist_array[current], |
|
270 item); |
|
271 } |
|
272 } |
|
273 |
|
274 if (current == -1) { /* empty history */ |
|
275 hist_info->head = 0; |
|
276 hist_info->current = -1; |
|
277 hist_info->last_serial = 0; |
|
278 }else{ |
|
279 Hist_item *item = (Hist_item *)(hist_info->hist_array[current])->data; |
|
280 hist_info->current = current; |
|
281 hist_info->last_serial = item->serial; |
|
282 } |
|
283 |
|
284 return; |
|
285 } |
|
286 |
|
287 /* write in-memory hist info into disk file */ |
|
288 static gboolean |
|
289 serialize_history_data (Hist_info *info) |
|
290 { |
|
291 gint head; |
|
292 gint current; |
|
293 |
|
294 if (NULL == info) |
|
295 return FALSE; |
|
296 |
|
297 if (! info->changed) |
|
298 return TRUE; |
|
299 |
|
300 if (info->current == -1) |
|
301 return TRUE; |
|
302 |
|
303 /* skip the head, cause head always pointer to NULL */ |
|
304 head = (info->head + 1) % info->max_len; |
|
305 current = (info->current + 1) % info->max_len; |
|
306 |
|
307 while (head != current) { |
|
308 g_list_foreach (info->hist_array[head], |
|
309 serialize_history_item, |
|
310 info->fp); |
|
311 |
|
312 head = (head + 1) % info->max_len; |
|
313 } |
|
314 |
|
315 return TRUE; |
|
316 } |
|
317 |
|
318 /* |
|
319 * Remove the last array element in history array |
|
320 */ |
|
321 void |
|
322 fsexam_history_remove_last (Hist_info *info) |
|
323 { |
|
324 gint current; |
|
325 |
|
326 if (NULL == info) |
|
327 return; |
|
328 |
|
329 current = info->current; |
|
330 |
|
331 if (current == -1) |
|
332 return; |
|
333 |
|
334 g_list_foreach (info->hist_array[current], release_each_item, NULL); |
|
335 g_list_free (info->hist_array[current]); |
|
336 |
|
337 info->changed = TRUE; |
|
338 info->hist_array[current] = NULL; |
|
339 |
|
340 info->current = (current + info->max_len - 1) % info->max_len; |
|
341 |
|
342 if (info->current == info->head) { /* empty now */ |
|
343 info->head = 0; |
|
344 info->current = -1; |
|
345 info->last_serial = 0; |
|
346 } else { |
|
347 Hist_item *item = (info->hist_array[info->current])->data; |
|
348 info->last_serial = item->serial; |
|
349 } |
|
350 |
|
351 return; |
|
352 } |
|
353 |
|
354 /* |
|
355 * Get the last conversion list in history file |
|
356 * For archive or compress file, we only get the compress or archive |
|
357 * file name |
|
358 */ |
|
359 GList * |
|
360 fsexam_history_get_last_list (Hist_info *info, ConvType *conv_type) |
|
361 { |
|
362 GList *file_list = NULL; |
|
363 GList *hist_list = NULL; |
|
364 GHashTable *special_file_hash = NULL; |
|
365 Hist_item *item = NULL; |
|
366 |
|
367 if (info == NULL) |
|
368 return NULL; |
|
369 |
|
370 if ((hist_list = info->hist_array[info->current])== NULL) |
|
371 return NULL; |
|
372 |
|
373 if (conv_type != NULL) { |
|
374 item = hist_list->data; |
|
375 *conv_type = item ? item->convtype : ConvInvalid; |
|
376 } |
|
377 |
|
378 while (hist_list) { |
|
379 gchar *filename = NULL; |
|
380 |
|
381 item = hist_list->data; |
|
382 filename = g_filename_from_uri (item->uri, NULL, NULL); |
|
383 |
|
384 if ((item->convtype == ConvNameSpecial) |
|
385 || (item->convtype == ConvContentSpecial)) { |
|
386 gint len = 0; |
|
387 gchar *tmp = filename; |
|
388 |
|
389 if (special_file_hash == NULL) { |
|
390 special_file_hash = g_hash_table_new_full ( |
|
391 g_str_hash, |
|
392 g_str_equal, |
|
393 (GDestroyNotify) g_free, |
|
394 NULL); |
|
395 } |
|
396 |
|
397 while ((*filename != '\t') && (*filename != '\0')) { |
|
398 filename ++; |
|
399 len ++; |
|
400 } |
|
401 |
|
402 filename = g_strndup (tmp, len); |
|
403 g_free (tmp); |
|
404 |
|
405 if (! g_hash_table_lookup (special_file_hash, filename)) { |
|
406 file_list = g_list_prepend (file_list, g_strdup (filename)); |
|
407 g_hash_table_insert (special_file_hash, filename, (gchar *)-1); |
|
408 } |
|
409 }else{ |
|
410 file_list = g_list_prepend (file_list, filename); |
|
411 } |
|
412 |
|
413 |
|
414 hist_list = g_list_next (hist_list); |
|
415 } |
|
416 |
|
417 if (special_file_hash != NULL) { |
|
418 g_hash_table_destroy (special_file_hash); |
|
419 } |
|
420 |
|
421 return file_list; |
|
422 } |
|
423 |
|
424 /* |
|
425 * put a history item in the info->histlist. |
|
426 */ |
|
427 void |
|
428 fsexam_history_put (Hist_info *info, |
|
429 ConvType convtype, |
|
430 const gchar *filename, |
|
431 gint from_encoding, |
|
432 gint to_encoding, |
|
433 gboolean same_serial) |
|
434 { |
|
435 if ((NULL == filename) || (NULL == info)) |
|
436 return; |
|
437 |
|
438 Hist_item *h = g_new0 (Hist_item, 1); |
|
439 |
|
440 h->convtype = convtype; |
|
441 h->from_encoding = from_encoding; |
|
442 h->to_encoding = to_encoding; |
|
443 h->uri = g_filename_to_uri (filename, NULL, NULL); |
|
444 |
|
445 if (! same_serial) { |
|
446 info->last_serial++; |
|
447 if (info->current == -1) |
|
448 info->current = 1; |
|
449 else |
|
450 info->current = (info->current + 1) % info->max_len; |
|
451 |
|
452 if (info->current == info->head) { |
|
453 reclaim_hist_elements (info); |
|
454 } |
|
455 |
|
456 } |
|
457 |
|
458 h->serial = info->last_serial; |
|
459 info->hist_array[info->current] = g_list_append ( |
|
460 info->hist_array[info->current], |
|
461 h); |
|
462 |
|
463 info->changed = TRUE; |
|
464 |
|
465 return; |
|
466 } |
|
467 |
|
468 /* |
|
469 * If the file specified by filename exist and open properly, |
|
470 * deserialize the data from file to list of History_items. |
|
471 */ |
|
472 Hist_info * |
|
473 fsexam_history_open (const gchar *filename, gint max_len) |
|
474 { |
|
475 struct stat stat_buf; |
|
476 Hist_info *info = NULL; |
|
477 |
|
478 if (NULL == filename) |
|
479 return NULL; |
|
480 |
|
481 info = g_new0 (Hist_info, 1); |
|
482 |
|
483 if (stat (filename, &stat_buf) == -1) |
|
484 info->fp = fopen (filename, "w+b"); /* create file if not exist */ |
|
485 else |
|
486 info->fp = fopen (filename, "r+b"); /* open for update */ |
|
487 |
|
488 /* allocate hist_array */ |
|
489 info->max_len = max_len; |
|
490 info->hist_array = g_new0 (GList *, info->max_len); |
|
491 |
|
492 /* Initial state of Hist_info */ |
|
493 info->head = 0; |
|
494 info->current = -1; |
|
495 info->last_serial = 0; |
|
496 info->changed = FALSE; |
|
497 |
|
498 deserialize_history_data (info); /* init history array */ |
|
499 |
|
500 if (fsexam_debug () & FSEXAM_DBG_HISTORY) { |
|
501 print_history (info); |
|
502 } |
|
503 |
|
504 return info; |
|
505 } |
|
506 |
|
507 /* |
|
508 * During search in the history list, we only differentiate |
|
509 * ConvName and non-ConvName |
|
510 */ |
|
511 Hist_item * |
|
512 fsexam_history_search (Hist_info *info, const gchar *path, gboolean convname) |
|
513 { |
|
514 if ((NULL == info) || (NULL == path)) |
|
515 return NULL; |
|
516 |
|
517 Hist_item *item = NULL; |
|
518 gint current = info->current; |
|
519 gint head = info->head; |
|
520 gchar *path_uri = NULL; |
|
521 |
|
522 if (current == -1) |
|
523 return NULL; |
|
524 |
|
525 path_uri = g_filename_to_uri (path, NULL, NULL); |
|
526 |
|
527 while (current != head) { |
|
528 GList *one_pass = info->hist_array[current]; |
|
529 |
|
530 while (one_pass) { |
|
531 Hist_item *tmp = (Hist_item *)one_pass->data; |
|
532 if ((tmp == NULL) || (tmp->uri == NULL)) |
|
533 continue; |
|
534 |
|
535 if ((strcmp (tmp->uri , path_uri) == 0) |
|
536 && ((convname && ((tmp->convtype == ConvName) |
|
537 || (tmp->convtype == ConvNameSpecial))) |
|
538 || (!convname |
|
539 && (tmp->convtype != ConvName) |
|
540 && (tmp->convtype != ConvNameSpecial)))) { |
|
541 item = tmp; /* found match */ |
|
542 } |
|
543 |
|
544 |
|
545 if (item != NULL) |
|
546 break; |
|
547 |
|
548 one_pass = g_list_next (one_pass); |
|
549 } |
|
550 |
|
551 if (item != NULL) |
|
552 break; |
|
553 |
|
554 current = (current - 1 + info->max_len) % info->max_len; |
|
555 } |
|
556 |
|
557 g_free (path_uri); |
|
558 |
|
559 return item; |
|
560 } |
|
561 |
|
562 /* |
|
563 * Get the latest serial |
|
564 */ |
|
565 gint32 |
|
566 fsexam_history_get_serial (Hist_info *info) |
|
567 { |
|
568 if (NULL == info) |
|
569 return -1; |
|
570 |
|
571 return info->last_serial; |
|
572 } |
|
573 |
|
574 /* |
|
575 * Serialize list of History_items, and save to info->fp. |
|
576 * At last, close info->fp, and release info->histlist. |
|
577 */ |
|
578 gboolean |
|
579 fsexam_history_close (Hist_info *info) |
|
580 { |
|
581 if (info->fp) { |
|
582 fseek (info->fp, SEEK_SET, 0); |
|
583 serialize_history_data (info); /* save data into disk */ |
|
584 fclose (info->fp); |
|
585 } |
|
586 |
|
587 release_hist_array (info); |
|
588 |
|
589 g_free (info->hist_array); |
|
590 g_free (info); |
|
591 |
|
592 return TRUE; |
|
593 } |
|
594 |