1 /* files.c -- file-related functions for Texinfo. 2 $Id: files.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ 3 4 Copyright (C) 1998, 99 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 #if O_BINARY || defined (VMS) 143 int n; 144 #endif 145 146 result = fullpath = NULL; 147 148 fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo); 149 150 if (!fullpath) 151 goto error_exit; 152 153 filename = fullpath; 154 file_size = (long) fileinfo.st_size; 155 156 file = open (filename, O_RDONLY); 157 if (file < 0) 158 goto error_exit; 159 160 /* Load the file, with enough room for a newline and a null. */ 161 result = xmalloc (file_size + 2); 162 163 /* VMS stat lies about the st_size value. The actual number of 164 readable bytes is always less than this value. The arcane 165 mysteries of VMS/RMS are too much to probe, so this hack 166 suffices to make things work. */ 167 #if O_BINARY || defined (VMS) 168 #ifdef VMS 169 while ((n = read (file, result + count, file_size)) > 0) 170 #else /* !VMS */ 171 #ifndef WIN32 172 while ((n = read (file, result + count, file_size)) > 0) 173 #else /* WIN32 */ 174 /* Does WIN32 really need reading 1 character at a time?? */ 175 while ((n = read (file, result + count, 1)) > 0) 176 #endif /* WIN32 */ 177 #endif /* !VMS */ 178 count += n; 179 if (0 < count && count < file_size) 180 result = xrealloc (result, count + 2); /* why waste the slack? */ 181 else if (n == -1) 182 #else /* !VMS && !O_BINARY */ 183 count = file_size; 184 if (read (file, result, file_size) != file_size) 185 #endif /* !VMS && !WIN32 */ 186 187 error_exit: 188 { 189 if (result) 190 free (result); 191 192 if (fullpath) 193 free (fullpath); 194 195 if (file != -1) 196 close (file); 197 198 return NULL; 199 } 200 close (file); 201 202 /* Set the globals to the new file. */ 203 input_text = result; 204 input_text_length = count; 205 input_filename = fullpath; 206 node_filename = xstrdup (fullpath); 207 input_text_offset = 0; 208 line_number = 1; 209 /* Not strictly necessary. This magic prevents read_token () from doing 210 extra unnecessary work each time it is called (that is a lot of times). 211 INPUT_TEXT_LENGTH is one past the actual end of the text. */ 212 input_text[input_text_length] = '\n'; 213 /* This, on the other hand, is always necessary. */ 214 input_text[input_text_length+1] = 0; 215 return result; 216 } 217 218 /* Pushing and popping files. */ 219 void 220 push_node_filename () 221 { 222 if (node_filename_stack_index + 1 > node_filename_stack_size) 223 node_filename_stack = xrealloc 224 (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); 225 226 node_filename_stack[node_filename_stack_index] = node_filename; 227 node_filename_stack_index++; 228 } 229 230 void 231 pop_node_filename () 232 { 233 node_filename = node_filename_stack[--node_filename_stack_index]; 234 } 235 236 /* Save the state of the current input file. */ 237 void 238 pushfile () 239 { 240 FSTACK *newstack = xmalloc (sizeof (FSTACK)); 241 newstack->filename = input_filename; 242 newstack->text = input_text; 243 newstack->size = input_text_length; 244 newstack->offset = input_text_offset; 245 newstack->line_number = line_number; 246 newstack->next = filestack; 247 248 filestack = newstack; 249 push_node_filename (); 250 } 251 252 /* Make the current file globals be what is on top of the file stack. */ 253 void 254 popfile () 255 { 256 FSTACK *tos = filestack; 257 258 if (!tos) 259 abort (); /* My fault. I wonder what I did? */ 260 261 if (macro_expansion_output_stream) 262 { 263 maybe_write_itext (input_text, input_text_offset); 264 forget_itext (input_text); 265 } 266 267 /* Pop the stack. */ 268 filestack = filestack->next; 269 270 /* Make sure that commands with braces have been satisfied. */ 271 if (!executing_string && !me_executing_string) 272 discard_braces (); 273 274 /* Get the top of the stack into the globals. */ 275 input_filename = tos->filename; 276 input_text = tos->text; 277 input_text_length = tos->size; 278 input_text_offset = tos->offset; 279 line_number = tos->line_number; 280 free (tos); 281 282 /* Go back to the (now) current node. */ 283 pop_node_filename (); 284 } 285 286 /* Flush all open files on the file stack. */ 287 void 288 flush_file_stack () 289 { 290 while (filestack) 291 { 292 char *fname = input_filename; 293 char *text = input_text; 294 popfile (); 295 free (fname); 296 free (text); 297 } 298 } 299 300 /* Return the index of the first character in the filename 301 which is past all the leading directory characters. */ 302 static int 303 skip_directory_part (filename) 304 char *filename; 305 { 306 int i = strlen (filename) - 1; 307 308 while (i && !IS_SLASH (filename[i])) 309 i--; 310 if (IS_SLASH (filename[i])) 311 i++; 312 else if (filename[i] && HAVE_DRIVE (filename)) 313 i = 2; 314 315 return i; 316 } 317 318 char * 319 filename_non_directory (name) 320 char *name; 321 { 322 return xstrdup (name + skip_directory_part (name)); 323 } 324 325 /* Return just the simple part of the filename; i.e. the 326 filename without the path information, or extensions. 327 This conses up a new string. */ 328 char * 329 filename_part (filename) 330 char *filename; 331 { 332 char *basename = filename_non_directory (filename); 333 334 #ifdef REMOVE_OUTPUT_EXTENSIONS 335 /* See if there is an extension to remove. If so, remove it. */ 336 { 337 char *temp; 338 339 temp = strrchr (basename, '.'); 340 if (temp) 341 *temp = 0; 342 } 343 #endif /* REMOVE_OUTPUT_EXTENSIONS */ 344 return basename; 345 } 346 347 /* Return the pathname part of filename. This can be NULL. */ 348 char * 349 pathname_part (filename) 350 char *filename; 351 { 352 char *expand_filename (); 353 char *result = NULL; 354 int i; 355 356 filename = expand_filename (filename, ""); 357 358 i = skip_directory_part (filename); 359 if (i) 360 { 361 result = xmalloc (1 + i); 362 strncpy (result, filename, i); 363 result[i] = 0; 364 } 365 free (filename); 366 return result; 367 } 368 369 /* Return the expansion of FILENAME. */ 370 char * 371 expand_filename (filename, input_name) 372 char *filename, *input_name; 373 { 374 int i; 375 char *full_pathname (); 376 377 if (filename) 378 { 379 filename = full_pathname (filename); 380 if (IS_ABSOLUTE (filename) 381 || (*filename == '.' && 382 (IS_SLASH (filename[1]) || 383 (filename[1] == '.' && IS_SLASH (filename[2]))))) 384 return filename; 385 } 386 else 387 { 388 filename = filename_non_directory (input_name); 389 390 if (!*filename) 391 { 392 free (filename); 393 filename = xstrdup ("noname.texi"); 394 } 395 396 for (i = strlen (filename) - 1; i; i--) 397 if (filename[i] == '.') 398 break; 399 400 if (!i) 401 i = strlen (filename); 402 403 if (i + 6 > (strlen (filename))) 404 filename = xrealloc (filename, i + 6); 405 strcpy (filename + i, html ? ".html" : ".info"); 406 return filename; 407 } 408 409 if (IS_ABSOLUTE (input_name)) 410 { 411 /* Make it so that relative names work. */ 412 char *result; 413 414 i = strlen (input_name) - 1; 415 416 result = xmalloc (1 + strlen (input_name) + strlen (filename)); 417 strcpy (result, input_name); 418 419 while (!IS_SLASH (result[i]) && i) 420 i--; 421 if (IS_SLASH (result[i])) 422 i++; 423 424 strcpy (&result[i], filename); 425 free (filename); 426 return result; 427 } 428 return filename; 429 } 430 431 /* Return the full path to FILENAME. */ 432 char * 433 full_pathname (filename) 434 char *filename; 435 { 436 int initial_character; 437 char *result; 438 439 /* No filename given? */ 440 if (!filename || !*filename) 441 return xstrdup (""); 442 443 /* Already absolute? */ 444 if (IS_ABSOLUTE (filename) || 445 (*filename == '.' && 446 (IS_SLASH (filename[1]) || 447 (filename[1] == '.' && IS_SLASH (filename[2]))))) 448 return xstrdup (filename); 449 450 initial_character = *filename; 451 if (initial_character != '~') 452 { 453 char *localdir = xmalloc (1025); 454 #ifdef HAVE_GETCWD 455 if (!getcwd (localdir, 1024)) 456 #else 457 if (!getwd (localdir)) 458 #endif 459 { 460 fprintf (stderr, _("%s: getwd: %s, %s\n"), 461 progname, filename, localdir); 462 xexit (1); 463 } 464 465 strcat (localdir, "/"); 466 strcat (localdir, filename); 467 result = xstrdup (localdir); 468 free (localdir); 469 } 470 else 471 { /* Does anybody know why WIN32 doesn't want to support $HOME? 472 If the reason is they don't have getpwnam, they should 473 only disable the else clause below. */ 474 #ifndef WIN32 475 if (IS_SLASH (filename[1])) 476 { 477 /* Return the concatenation of the environment variable HOME 478 and the rest of the string. */ 479 char *temp_home; 480 481 temp_home = (char *) getenv ("HOME"); 482 result = xmalloc (strlen (&filename[1]) 483 + 1 484 + temp_home ? strlen (temp_home) 485 : 0); 486 *result = 0; 487 488 if (temp_home) 489 strcpy (result, temp_home); 490 491 strcat (result, &filename[1]); 492 } 493 else 494 { 495 struct passwd *user_entry; 496 int i, c; 497 char *username = xmalloc (257); 498 499 for (i = 1; (c = filename[i]); i++) 500 { 501 if (IS_SLASH (c)) 502 break; 503 else 504 username[i - 1] = c; 505 } 506 if (c) 507 username[i - 1] = 0; 508 509 user_entry = getpwnam (username); 510 511 if (!user_entry) 512 return xstrdup (filename); 513 514 result = xmalloc (1 + strlen (user_entry->pw_dir) 515 + strlen (&filename[i])); 516 strcpy (result, user_entry->pw_dir); 517 strcat (result, &filename[i]); 518 } 519 #endif /* not WIN32 */ 520 } 521 return result; 522 } 523 524 char * 525 output_name_from_input_name (name) 526 char *name; 527 { 528 return expand_filename (NULL, name); 529 } 530