1 /* files.c -- file-related functions for makeinfo. 2 $Id: files.c,v 1.1.1.2 2002/06/10 13:21:15 espie Exp $ 3 4 Copyright (C) 1998, 99, 2000, 01, 02 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software Foundation, 18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 19 20 #include "system.h" 21 #include "files.h" 22 #include "macro.h" 23 #include "makeinfo.h" 24 25 FSTACK *filestack = NULL; 26 27 static int node_filename_stack_index = 0; 28 static int node_filename_stack_size = 0; 29 static char **node_filename_stack = NULL; 30 31 32 /* Looking for include files. */ 33 34 /* Given a string containing units of information separated by colons, 35 return the next one pointed to by INDEX, or NULL if there are no more. 36 Advance INDEX to the character after the colon. */ 37 static char * 38 extract_colon_unit (string, index) 39 char *string; 40 int *index; 41 { 42 int start; 43 int path_sep_char = PATH_SEP[0]; 44 int i = *index; 45 46 if (!string || (i >= strlen (string))) 47 return NULL; 48 49 /* Each call to this routine leaves the index pointing at a colon if 50 there is more to the path. If i > 0, then increment past the 51 `:'. If i == 0, then the path has a leading colon. Trailing colons 52 are handled OK by the `else' part of the if statement; an empty 53 string is returned in that case. */ 54 if (i && string[i] == path_sep_char) 55 i++; 56 57 start = i; 58 while (string[i] && string[i] != path_sep_char) i++; 59 *index = i; 60 61 if (i == start) 62 { 63 if (string[i]) 64 (*index)++; 65 66 /* Return "" in the case of a trailing `:'. */ 67 return xstrdup (""); 68 } 69 else 70 { 71 char *value; 72 73 value = xmalloc (1 + (i - start)); 74 memcpy (value, &string[start], (i - start)); 75 value [i - start] = 0; 76 77 return value; 78 } 79 } 80 81 /* Return the full pathname for FILENAME by searching along PATH. 82 When found, return the stat () info for FILENAME in FINFO. 83 If PATH is NULL, only the current directory is searched. 84 If the file could not be found, return a NULL pointer. */ 85 static char * 86 get_file_info_in_path (filename, path, finfo) 87 char *filename, *path; 88 struct stat *finfo; 89 { 90 char *dir; 91 int result, index = 0; 92 93 if (path == NULL) 94 path = "."; 95 96 /* Handle absolute pathnames. */ 97 if (IS_ABSOLUTE (filename) 98 || (*filename == '.' 99 && (IS_SLASH (filename[1]) 100 || (filename[1] == '.' && IS_SLASH (filename[2]))))) 101 { 102 if (stat (filename, finfo) == 0) 103 return xstrdup (filename); 104 else 105 return NULL; 106 } 107 108 while ((dir = extract_colon_unit (path, &index))) 109 { 110 char *fullpath; 111 112 if (!*dir) 113 { 114 free (dir); 115 dir = xstrdup ("."); 116 } 117 118 fullpath = xmalloc (2 + strlen (dir) + strlen (filename)); 119 sprintf (fullpath, "%s/%s", dir, filename); 120 free (dir); 121 122 result = stat (fullpath, finfo); 123 124 if (result == 0) 125 return fullpath; 126 else 127 free (fullpath); 128 } 129 return NULL; 130 } 131 132 /* Find and load the file named FILENAME. Return a pointer to 133 the loaded file, or NULL if it can't be loaded. */ 134 char * 135 find_and_load (filename) 136 char *filename; 137 { 138 struct stat fileinfo; 139 long file_size; 140 int file = -1, count = 0; 141 char *fullpath, *result; 142 int n, bytes_to_read; 143 144 result = fullpath = NULL; 145 146 fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo); 147 148 if (!fullpath) 149 goto error_exit; 150 151 filename = fullpath; 152 file_size = (long) fileinfo.st_size; 153 154 file = open (filename, O_RDONLY); 155 if (file < 0) 156 goto error_exit; 157 158 /* Load the file, with enough room for a newline and a null. */ 159 result = xmalloc (file_size + 2); 160 161 /* VMS stat lies about the st_size value. The actual number of 162 readable bytes is always less than this value. The arcane 163 mysteries of VMS/RMS are too much to probe, so this hack 164 suffices to make things work. It's also needed on Cygwin. And so 165 we might as well use it everywhere. */ 166 bytes_to_read = file_size; 167 while ((n = read (file, result + count, bytes_to_read)) > 0) 168 { 169 count += n; 170 bytes_to_read -= n; 171 } 172 if (0 < count && count < file_size) 173 result = xrealloc (result, count + 2); /* why waste the slack? */ 174 else if (n == -1) 175 error_exit: 176 { 177 if (result) 178 free (result); 179 180 if (fullpath) 181 free (fullpath); 182 183 if (file != -1) 184 close (file); 185 186 return NULL; 187 } 188 close (file); 189 190 /* Set the globals to the new file. */ 191 input_text = result; 192 input_text_length = count; 193 input_filename = fullpath; 194 node_filename = xstrdup (fullpath); 195 input_text_offset = 0; 196 line_number = 1; 197 /* Not strictly necessary. This magic prevents read_token () from doing 198 extra unnecessary work each time it is called (that is a lot of times). 199 INPUT_TEXT_LENGTH is one past the actual end of the text. */ 200 input_text[input_text_length] = '\n'; 201 /* This, on the other hand, is always necessary. */ 202 input_text[input_text_length+1] = 0; 203 return result; 204 } 205 206 /* Pushing and popping files. */ 207 void 208 push_node_filename () 209 { 210 if (node_filename_stack_index + 1 > node_filename_stack_size) 211 node_filename_stack = xrealloc 212 (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); 213 214 node_filename_stack[node_filename_stack_index] = node_filename; 215 node_filename_stack_index++; 216 } 217 218 void 219 pop_node_filename () 220 { 221 node_filename = node_filename_stack[--node_filename_stack_index]; 222 } 223 224 /* Save the state of the current input file. */ 225 void 226 pushfile () 227 { 228 FSTACK *newstack = xmalloc (sizeof (FSTACK)); 229 newstack->filename = input_filename; 230 newstack->text = input_text; 231 newstack->size = input_text_length; 232 newstack->offset = input_text_offset; 233 newstack->line_number = line_number; 234 newstack->next = filestack; 235 236 filestack = newstack; 237 push_node_filename (); 238 } 239 240 /* Make the current file globals be what is on top of the file stack. */ 241 void 242 popfile () 243 { 244 FSTACK *tos = filestack; 245 246 if (!tos) 247 abort (); /* My fault. I wonder what I did? */ 248 249 if (macro_expansion_output_stream) 250 { 251 maybe_write_itext (input_text, input_text_offset); 252 forget_itext (input_text); 253 } 254 255 /* Pop the stack. */ 256 filestack = filestack->next; 257 258 /* Make sure that commands with braces have been satisfied. */ 259 if (!executing_string && !me_executing_string) 260 discard_braces (); 261 262 /* Get the top of the stack into the globals. */ 263 input_filename = tos->filename; 264 input_text = tos->text; 265 input_text_length = tos->size; 266 input_text_offset = tos->offset; 267 line_number = tos->line_number; 268 free (tos); 269 270 /* Go back to the (now) current node. */ 271 pop_node_filename (); 272 } 273 274 /* Flush all open files on the file stack. */ 275 void 276 flush_file_stack () 277 { 278 while (filestack) 279 { 280 char *fname = input_filename; 281 char *text = input_text; 282 popfile (); 283 free (fname); 284 free (text); 285 } 286 } 287 288 /* Return the index of the first character in the filename 289 which is past all the leading directory characters. */ 290 static int 291 skip_directory_part (filename) 292 char *filename; 293 { 294 int i = strlen (filename) - 1; 295 296 while (i && !IS_SLASH (filename[i])) 297 i--; 298 if (IS_SLASH (filename[i])) 299 i++; 300 else if (filename[i] && HAVE_DRIVE (filename)) 301 i = 2; 302 303 return i; 304 } 305 306 char * 307 filename_non_directory (name) 308 char *name; 309 { 310 return xstrdup (name + skip_directory_part (name)); 311 } 312 313 /* Return just the simple part of the filename; i.e. the 314 filename without the path information, or extensions. 315 This conses up a new string. */ 316 char * 317 filename_part (filename) 318 char *filename; 319 { 320 char *basename = filename_non_directory (filename); 321 322 #ifdef REMOVE_OUTPUT_EXTENSIONS 323 /* See if there is an extension to remove. If so, remove it. */ 324 { 325 char *temp; 326 327 temp = strrchr (basename, '.'); 328 if (temp) 329 *temp = 0; 330 } 331 #endif /* REMOVE_OUTPUT_EXTENSIONS */ 332 return basename; 333 } 334 335 /* Return the pathname part of filename. This can be NULL. */ 336 char * 337 pathname_part (filename) 338 char *filename; 339 { 340 char *expand_filename (); 341 char *result = NULL; 342 int i; 343 344 filename = expand_filename (filename, ""); 345 346 i = skip_directory_part (filename); 347 if (i) 348 { 349 result = xmalloc (1 + i); 350 strncpy (result, filename, i); 351 result[i] = 0; 352 } 353 free (filename); 354 return result; 355 } 356 357 /* Return the expansion of FILENAME. */ 358 char * 359 expand_filename (filename, input_name) 360 char *filename, *input_name; 361 { 362 int i; 363 char *full_pathname (); 364 365 if (filename) 366 { 367 filename = full_pathname (filename); 368 if (IS_ABSOLUTE (filename) 369 || (*filename == '.' && 370 (IS_SLASH (filename[1]) || 371 (filename[1] == '.' && IS_SLASH (filename[2]))))) 372 return filename; 373 } 374 else 375 { 376 filename = filename_non_directory (input_name); 377 378 if (!*filename) 379 { 380 free (filename); 381 filename = xstrdup ("noname.texi"); 382 } 383 384 for (i = strlen (filename) - 1; i; i--) 385 if (filename[i] == '.') 386 break; 387 388 if (!i) 389 i = strlen (filename); 390 391 if (i + 6 > (strlen (filename))) 392 filename = xrealloc (filename, i + 6); 393 strcpy (filename + i, html ? ".html" : ".info"); 394 return filename; 395 } 396 397 if (IS_ABSOLUTE (input_name)) 398 { 399 /* Make it so that relative names work. */ 400 char *result; 401 402 i = strlen (input_name) - 1; 403 404 result = xmalloc (1 + strlen (input_name) + strlen (filename)); 405 strcpy (result, input_name); 406 407 while (!IS_SLASH (result[i]) && i) 408 i--; 409 if (IS_SLASH (result[i])) 410 i++; 411 412 strcpy (&result[i], filename); 413 free (filename); 414 return result; 415 } 416 return filename; 417 } 418 419 /* Return the full path to FILENAME. */ 420 char * 421 full_pathname (filename) 422 char *filename; 423 { 424 int initial_character; 425 char *result; 426 427 /* No filename given? */ 428 if (!filename || !*filename) 429 return xstrdup (""); 430 431 /* Already absolute? */ 432 if (IS_ABSOLUTE (filename) || 433 (*filename == '.' && 434 (IS_SLASH (filename[1]) || 435 (filename[1] == '.' && IS_SLASH (filename[2]))))) 436 return xstrdup (filename); 437 438 initial_character = *filename; 439 if (initial_character != '~') 440 { 441 char *localdir = xmalloc (1025); 442 #ifdef HAVE_GETCWD 443 if (!getcwd (localdir, 1024)) 444 #else 445 if (!getwd (localdir)) 446 #endif 447 { 448 fprintf (stderr, _("%s: getwd: %s, %s\n"), 449 progname, filename, localdir); 450 xexit (1); 451 } 452 453 strcat (localdir, "/"); 454 strcat (localdir, filename); 455 result = xstrdup (localdir); 456 free (localdir); 457 } 458 else 459 { /* Does anybody know why WIN32 doesn't want to support $HOME? 460 If the reason is they don't have getpwnam, they should 461 only disable the else clause below. */ 462 #ifndef WIN32 463 if (IS_SLASH (filename[1])) 464 { 465 /* Return the concatenation of the environment variable HOME 466 and the rest of the string. */ 467 char *temp_home; 468 469 temp_home = (char *) getenv ("HOME"); 470 result = xmalloc (strlen (&filename[1]) 471 + 1 472 + temp_home ? strlen (temp_home) 473 : 0); 474 *result = 0; 475 476 if (temp_home) 477 strcpy (result, temp_home); 478 479 strcat (result, &filename[1]); 480 } 481 else 482 { 483 struct passwd *user_entry; 484 int i, c; 485 char *username = xmalloc (257); 486 487 for (i = 1; (c = filename[i]); i++) 488 { 489 if (IS_SLASH (c)) 490 break; 491 else 492 username[i - 1] = c; 493 } 494 if (c) 495 username[i - 1] = 0; 496 497 user_entry = getpwnam (username); 498 499 if (!user_entry) 500 return xstrdup (filename); 501 502 result = xmalloc (1 + strlen (user_entry->pw_dir) 503 + strlen (&filename[i])); 504 strcpy (result, user_entry->pw_dir); 505 strcat (result, &filename[i]); 506 } 507 #endif /* not WIN32 */ 508 } 509 return result; 510 } 511 512 char * 513 output_name_from_input_name (name) 514 char *name; 515 { 516 return expand_filename (NULL, name); 517 } 518 519 520 /* Modify the file name FNAME so that it fits the limitations of the 521 underlying filesystem. In particular, truncate the file name as it 522 would be truncated by the filesystem. We assume the result can 523 never be longer than the original, otherwise we couldn't be sure we 524 have enough space in the original string to modify it in place. */ 525 char * 526 normalize_filename (fname) 527 char *fname; 528 { 529 int maxlen; 530 char orig[PATH_MAX + 1]; 531 int i; 532 char *lastdot, *p; 533 534 #ifdef _PC_NAME_MAX 535 maxlen = pathconf (fname, _PC_NAME_MAX); 536 if (maxlen < 1) 537 #endif 538 maxlen = PATH_MAX; 539 540 i = skip_directory_part (fname); 541 if (fname[i] == '\0') 542 return fname; /* only a directory name -- don't modify */ 543 strcpy (orig, fname + i); 544 545 switch (maxlen) 546 { 547 case 12: /* MS-DOS 8+3 filesystem */ 548 if (orig[0] == '.') /* leading dots are not allowed */ 549 orig[0] = '_'; 550 lastdot = strrchr (orig, '.'); 551 if (!lastdot) 552 lastdot = orig + strlen (orig); 553 strncpy (fname + i, orig, lastdot - orig); 554 for (p = fname + i; 555 p < fname + i + (lastdot - orig) && p < fname + i + 8; 556 p++) 557 if (*p == '.') 558 *p = '_'; 559 *p = '\0'; 560 if (*lastdot == '.') 561 strncat (fname + i, lastdot, 4); 562 break; 563 case 14: /* old Unix systems with 14-char limitation */ 564 strcpy (fname + i, orig); 565 if (strlen (fname + i) > 14) 566 fname[i + 14] = '\0'; 567 break; 568 default: 569 strcpy (fname + i, orig); 570 if (strlen (fname) > maxlen - 1) 571 fname[maxlen - 1] = '\0'; 572 break; 573 } 574 575 return fname; 576 } 577