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