1/*
2 * rarian-main.c
3 * This file is part of Rarian
4 *
5 * Copyright (C) 2006 - Don Scorgie
6 *
7 * Rarian is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * Rarian is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22#include <stdlib.h>
23#include <string.h>
24#include <dirent.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <sys/stat.h>
29
30#include "config.h"
31
32#include "rarian.h"
33#include "rarian-reg-utils.h"
34#include "rarian-language.h"
35#include "rarian-utils.h"
36#if ENABLE_OMF_READ
37#include "rarian-omf.h"
38#endif
39
40#ifndef FALSE
41#define FALSE 0
42#define TRUE !FALSE
43#endif
44
45/* Internal structures and lists */
46
47typedef struct _Link Link;
48
49
50struct _Link
51{
52	union {
53	    RrnReg *reg;
54		RrnSect *sect;
55		} reg;
56    Link *next;
57    Link *prev;
58};
59
60static Link * head = NULL;
61static Link * tail = NULL;
62
63static Link *orphans_head = NULL;
64static Link *orphans_tail = NULL;
65
66/* Function Prototypes */
67
68static void rrn_init        (void);
69static void scan_directories  (void);
70static void scan_directory    (char *dir);
71static void process_file      (char *filename);
72static void process_section   (char *filename);
73static void insert_orphans    (void);
74static void reverse_children  (void);
75static void process_locale_dirs (char * dir);
76#if ENABLE_OMF_READ
77static void process_omf_dir     (char *dir);
78#endif
79
80void
81rrn_set_language (char *lang_code)
82{
83  if (head) {
84    rrn_shutdown ();
85  }
86  rrn_language_init (lang_code);
87  rrn_init ();
88}
89
90static void
91rrn_init (void)
92{
93    scan_directories ();
94
95    return;
96}
97
98void
99rrn_for_each (RrnForeachFunc funct, void * user_data)
100{
101    Link *iter;
102
103    if (!head) {
104    	rrn_init ();
105    }
106
107    iter = head;
108
109    while (iter) {
110        int res;
111        res = funct (iter->reg.reg, user_data);
112        if (res == FALSE)
113            break;
114        iter = iter->next;
115    }
116
117    return;
118}
119
120void rrn_for_each_in_category (RrnForeachFunc funct, char * category,
121				 void *user_data)
122{
123    Link *iter;
124
125    if (!head) {
126    	rrn_init ();
127    }
128    iter = head;
129
130    while (iter) {
131        int res;
132	char **cats;
133
134	cats = iter->reg.reg->categories;
135	while (cats && *cats) {
136	  if (!strcmp(*cats, category)) {
137	    res = funct (iter->reg.reg, user_data);
138	    if (res == FALSE)
139	      break;
140	  }
141	  cats++;
142	}
143        iter = iter->next;
144    }
145
146    return;
147
148}
149
150RrnReg *
151rrn_find_entry_from_uri (char *uri)
152{
153    Link *iter;
154
155    if (!head) {
156    	rrn_init ();
157    }
158    iter = head;
159
160    while (iter) {
161
162      if (!strcmp(iter->reg.reg->uri, uri))
163	return iter->reg.reg;
164      iter = iter->next;
165    }
166
167    return NULL;
168}
169
170static void
171scan_directories (void)
172{
173  char *cur_path = NULL;
174
175#if ENABLE_INSTALL
176    char *path = NULL;
177    char *first_colon = NULL;
178	char *next_colon = NULL;
179    char *home_dir = NULL;
180    char *home_data_dir = NULL;
181    char *home_env = NULL;
182
183    home_env = getenv ("XDG_DATA_HOME");
184    if (home_env)
185      home_data_dir = strdup(home_env);
186
187    if (!home_data_dir || !strcmp (home_data_dir, "")) {
188        home_dir = getenv ("HOME");
189        if (!home_dir || !strcmp (home_dir, "")) {
190            fprintf (stderr, "Warning: HOME dir is not defined."
191                     "  Skipping check of XDG_DATA_HOME");
192            goto past;
193        }
194        home_data_dir = malloc (sizeof(char) * (strlen(home_dir)+14));
195        sprintf (home_data_dir, "%s/.local/share", home_dir);
196    }
197
198    /* Reuse home_dir.  Bad.*/
199    home_dir = malloc (sizeof (char) * (strlen(home_data_dir)+6));
200
201    sprintf (home_dir, "%s/help", home_data_dir);
202
203#if ENABLE_OMF_READ
204    process_omf_dir (home_data_dir);
205#endif
206
207    free (home_data_dir);
208
209    process_locale_dirs (home_dir);
210    scan_directory (home_dir);
211
212    free (home_dir);
213
214past:
215    path = getenv ("XDG_DATA_DIRS");
216
217    if (!path || !strcmp (path, "")) {
218        path = "@DEFAULT_DATA_DIRS@";
219    }
220    cur_path = path;
221
222    do {
223    	char *int_path = NULL;
224    	char *check_path = NULL;
225	    first_colon = strchr (cur_path, ':');
226	    if (first_colon)
227	      int_path = rrn_strndup (cur_path, (first_colon-cur_path));
228	    else
229	      int_path = strdup (cur_path);
230		check_path = malloc (sizeof(char)*(strlen(int_path)+6));
231		sprintf (check_path, "%s/help", int_path);
232#if ENABLE_OMF_READ
233		process_omf_dir (int_path);
234#endif
235		process_locale_dirs (check_path);
236
237		scan_directory (check_path);
238		if (int_path && *int_path) {
239			free (int_path);
240		}
241		if (check_path) {
242			free (check_path);
243		}
244		cur_path = first_colon+1;
245	} while (first_colon);
246#else
247	cur_path = "data/sk-import";
248	process_locale_dirs (cur_path);
249	scan_directory (cur_path);
250#endif
251	reverse_children ();
252}
253
254static void
255process_locale_dirs (char * dir)
256{
257	DIR *dirp = NULL;
258	char **paths_to_check = NULL;
259	char **iter = NULL;
260
261	paths_to_check = rrn_language_get_dirs (dir);
262	iter = paths_to_check;
263
264	while (*iter) {
265		scan_directory (*iter);
266		free (*iter);
267		iter++;
268	}
269	free (paths_to_check);
270
271}
272
273static void
274scan_directory (char *dir)
275{
276    DIR * dirp = NULL;
277    struct dirent * dp = NULL;
278    struct stat buf;
279    char *path = NULL;
280    dirp = opendir (dir);
281
282    if (access (dir, R_OK)) {
283    	return;
284    }
285    while (1) {
286      if ((dp = readdir(dirp)) != NULL) {
287	char *full_name = NULL;
288	full_name = malloc (sizeof(char)*(strlen (dp->d_name) + strlen(dir) + 2));
289
290	sprintf (full_name, "%s/%s", dir, dp->d_name);
291	stat(full_name,&buf);
292
293	if (S_ISREG(buf.st_mode)) {
294	  char *suffix = NULL;
295
296	  suffix = strrchr (full_name, '.');
297	  if (!strcmp (suffix, ".document")) {
298	    process_file (full_name);
299	  } else if (!strcmp (suffix, ".section")) {
300	    process_section (full_name);
301	  }
302	} else if (S_ISDIR(buf.st_mode) && strcmp (dp->d_name, ".") &&
303		   strcmp (dp->d_name, "..") &&
304		   strcmp (dp->d_name, "LOCALE")) {
305
306	  scan_directory (full_name);
307	}
308	free (full_name);
309      } else {
310	goto done;
311      }
312    }
313
314done:
315	insert_orphans ();
316    closedir (dirp);
317    free (path);
318}
319
320static int
321handle_duplicate (RrnReg *reg)
322{
323    Link *iter;
324
325    iter = head;
326
327    while (iter) {
328      if ((iter->reg.reg->heritage && reg->heritage &&
329	   !strcmp (iter->reg.reg->heritage, reg->heritage)) ||
330	  !strcmp (iter->reg.reg->identifier, reg->identifier)) {
331	if (iter->reg.reg->lang && reg->lang &&
332	    rrn_language_use (iter->reg.reg->lang, reg->lang)) {
333	  rrn_reg_free (iter->reg.reg);
334	  iter->reg.reg = reg;
335	}
336	return TRUE;
337      }
338      iter = iter->next;
339    }
340
341    return FALSE;
342
343}
344
345#if ENABLE_OMF_READ
346static void
347process_omf_dir (char *dir)
348{
349  char *path;
350  DIR * dirp = NULL;
351  char **langs = NULL;
352  char **langs_iter = NULL;
353  int lang_found = FALSE;
354  int lang_count = 0;
355  char *tmp = NULL;
356
357  struct dirent * dp = NULL;
358  struct stat buf;
359
360  langs = rrn_language_get_langs ();
361  path = malloc (sizeof(char) * (strlen (dir)+6));
362
363  sprintf (path, "%s/omf", dir);
364
365  if (access (path, R_OK)) {
366    return;
367  }
368
369  langs_iter = langs;
370  while (langs_iter && *langs_iter) {
371    lang_count++;
372    if (!strcmp (*langs_iter, "C")) {
373	lang_found = TRUE;
374      }
375    langs_iter++;
376  }
377  if (!lang_found) {
378    char **tmp;
379    int i = 0;
380    tmp = malloc (sizeof (char *) * (lang_count+2));
381    langs_iter = langs;
382    while (langs_iter && *langs_iter) {
383      tmp[i] = strdup (*langs_iter);
384      i++;
385      langs_iter++;
386    }
387    tmp[i] = strdup ("C");
388    i++;
389    tmp[i] = NULL;
390    langs = tmp;
391  }
392
393
394  dirp = opendir (path);
395
396  while (1) {
397    if ((dp = readdir(dirp)) != NULL) {
398      char *full_name;
399      full_name = malloc (sizeof(char) * (strlen(path) + strlen(dp->d_name) + 5));
400    sprintf (full_name, "%s/%s", path, dp->d_name);
401      stat(full_name,&buf);
402      free (full_name);
403      if (S_ISDIR(buf.st_mode) && strcmp (dp->d_name, ".") &&
404	  strcmp (dp->d_name, "..")) {
405	langs_iter = langs;
406	while (langs_iter && *langs_iter) {
407	  char *lang = (*langs_iter);
408	  /* Add extra 2 for separator and NULL.  Otherwise, it falls over */
409	  tmp = malloc (sizeof (char) * (strlen(dir)+(strlen(dp->d_name)*2) +
410					 strlen(lang) + 20));
411	  sprintf (tmp, "%s/%s/%s-%s.omf", path, dp->d_name, dp->d_name,(*langs_iter));
412
413	  if (!access (tmp, R_OK)) {
414	    RrnReg *reg = NULL;
415	    reg = rrn_omf_parse_file (tmp);
416	    if (reg) {
417	      reg->omf_location = strdup (tmp);
418	      reg->ghelp_name = strdup (dp->d_name);
419	    }
420	    if (reg && !handle_duplicate (reg)) {
421	      Link *link;
422
423	      link = malloc (sizeof (Link));
424	      link->reg.reg = reg;
425	      link->next = NULL;
426
427	      if (!tail) {
428		if (head) {
429		  fprintf (stderr, "ERROR: Tail not pointing anywhere.  "
430			   "Aborting");
431		  exit (3);
432		}
433		head = link;
434		tail = link;
435	      } else {
436		tail->next = link;
437		tail = link;
438
439	      }
440	    }
441	  }
442	  free (tmp);
443	  tmp = NULL;
444	  langs_iter++;
445	}
446      }
447    } else {
448      break;
449    }
450  }
451done:
452  insert_orphans ();
453  closedir (dirp);
454}
455#endif
456
457static void
458process_section (char *filename)
459{
460	RrnSect *sect = NULL;
461	Link *link;
462
463	sect = rrn_sect_parse_file (filename);
464	if (!sect)
465		return;
466
467	link = malloc (sizeof (Link));
468    link->reg.sect = sect;
469    link->next = NULL;
470    link->prev = NULL;
471
472	if (!orphans_head) {
473		orphans_head = link;
474		orphans_tail = link;
475	} else {
476		orphans_tail->next = link;
477		link->prev = orphans_tail;
478		orphans_tail = link;
479	}
480}
481
482static void
483process_file (char *filename)
484{
485    RrnReg *reg;
486    Link *link;
487
488    reg = rrn_reg_parse_file (filename);
489    if (!reg)
490        return;
491
492	if (handle_duplicate (reg)) {
493	  return;
494	}
495
496    link = malloc (sizeof (Link));
497    link->reg.reg = reg;
498    link->next = NULL;
499
500    if (!tail) {
501        if (head) {
502            fprintf (stderr, "ERROR: Tail not pointing anywhere.  Aborting");
503            exit (3);
504        }
505        head = link;
506        tail = link;
507    } else {
508        tail->next = link;
509        tail = link;
510
511    }
512}
513
514static void
515insert_orphans ()
516{
517	Link *sect = orphans_head;
518
519	while (sect) {
520    	Link *iter = head;
521
522	    while (iter) {
523	    	if (!strncmp (iter->reg.reg->identifier, sect->reg.sect->owner,
524	    				  strlen(iter->reg.reg->identifier))) {
525				break;
526			}
527			iter = iter->next;
528		}
529		if (iter) {
530			sect->reg.sect = rrn_reg_add_sections (iter->reg.reg,
531								 sect->reg.sect);
532			if (sect->reg.sect == NULL) {
533				Link *tmp = sect->next;
534				if (sect->prev)
535					sect->prev->next = sect->next;
536				if (sect->next)
537					sect->next->prev = sect->prev;
538				if (sect == orphans_head)
539					orphans_head = NULL;
540				free (sect);
541				sect = tmp;
542			}
543		} else {
544			sect->reg.sect->priority++;
545			sect = sect->next;
546		}
547	}
548}
549
550static RrnSect *
551reverse_child (RrnSect *child)
552{
553	RrnSect *local_tail = NULL;
554	RrnSect *iter = child;
555	RrnSect *tmp = NULL;
556
557	while (iter) {
558		if (iter->children)
559			iter->children = reverse_child (iter->children);
560		tmp = iter->next;
561		iter->next = iter->prev;
562		iter->prev = tmp;
563		if (iter->prev == NULL) {
564			return iter;
565		}
566		iter = iter->prev;
567	}
568}
569
570static void
571reverse_children  ()
572{
573	Link *iter = head;
574
575	while (iter) {
576		if (iter->reg.reg->children) {
577			iter->reg.reg->children = reverse_child (iter->reg.reg->children);
578		}
579
580		iter = iter->next;
581	}
582}
583
584RrnReg *
585rrn_find_from_name (char *name)
586{
587  if (!head)
588    rrn_init ();
589
590  return NULL;
591
592
593}
594
595RrnReg *
596rrn_find_from_ghelp (char *ghelp)
597{
598    Link *iter;
599
600    if (!head) {
601    	rrn_init ();
602    }
603    iter = head;
604
605    while (iter) {
606      if (iter->reg.reg->ghelp_name && !strcmp(iter->reg.reg->ghelp_name, ghelp))
607	return iter->reg.reg;
608      iter = iter->next;
609    }
610
611    return NULL;
612}
613
614
615void
616rrn_shutdown ()
617{
618    Link *next;
619
620    while (head) {
621        next = head->next;
622
623        rrn_reg_free (head->reg.reg);
624        free (head);
625        head = next;
626    }
627    rrn_language_shutdown ();
628    head = tail = NULL;
629    return;
630}
631