499 lines
13 KiB
C
Executable File
499 lines
13 KiB
C
Executable File
#ident "@(#)file.c 1.2 95/09/07 SMI"
|
|
/*
|
|
* Copyright (c) 1986, 1987, 1988, Sun Microsystems, Inc.
|
|
* All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* File: file.c
|
|
* Date: Sun Feb 12 19:12:01 PST 1995
|
|
*
|
|
* Description:
|
|
*
|
|
* This file contain routines for file management / manipulation.
|
|
* Each opened file is converted to wide character format and
|
|
* stored in P_tmpdir as an intermediate file until the program exits.
|
|
* Files previously opened will use the intermediate version
|
|
* of the file and the original version is ignored.
|
|
*
|
|
* All data manipulation performed by insert_line, and delete_line
|
|
* is done to the intermediate version of the file.
|
|
*
|
|
* Fetch_line maps the appropriate page of the temp files into
|
|
* memory and returns the pointer to the requested line.
|
|
*
|
|
* Data will not be written out to a file until sync_file is called.
|
|
* sync_file will not alter the state of the intermediate file.
|
|
*
|
|
* These algorithms enable patch to process any arbitrary number of files
|
|
* and allow each and every file to be modified endlessly in any order
|
|
* specified by the patch file "Index:" lines and/or contest diff file
|
|
* lines. Additional hooks have been placed in the sync_file routine
|
|
* to force writing to a specific file for the case were the output
|
|
* file is specifically specified with the -o option.
|
|
*
|
|
* A visual view the data structures in action:
|
|
*
|
|
* opened_file_descriptors = 3
|
|
*
|
|
* file_info **opened_file
|
|
* +------------+
|
|
* | |--------->+---------------------------------+
|
|
* +------------+ | char *name = "patch_file"|
|
|
* | |------+ +---------------------------------+
|
|
* +------------+ | | ... |
|
|
* | |----+ | +---------------------------------+
|
|
* +------------+ | | | ulong mapped_page = 0 |
|
|
* | | +---------------------------------+
|
|
* | | | caddr_t mapped_address |
|
|
* | | +---------------------------------+
|
|
* | | | line_address *lines[] |
|
|
* | | +---------------------------------+
|
|
* | |
|
|
* | +-->+---------------------------------+
|
|
* | | char *name = "file1" |
|
|
* | +---------------------------------+
|
|
* | | ... |
|
|
* | +---------------------------------+
|
|
* | | ulong mapped_page = 99 |
|
|
* | +---------------------------------+
|
|
* | | caddr_t mapped_address |
|
|
* | +---------------------------------+
|
|
* | | line_address *lines[] |
|
|
* | +---------------------------------+
|
|
* |
|
|
* +---->+---------------------------------+
|
|
* | char *name = "file2" |
|
|
* +---------------------------------+
|
|
* | ... |
|
|
* +---------------------------------+
|
|
* | ulong mapped_page = 10 |
|
|
* +---------------------------------+
|
|
* | caddr_t mapped_address |
|
|
* +---------------------------------+
|
|
* | line_address *lines[] |
|
|
* +---------------------------------+
|
|
*/
|
|
|
|
/*
|
|
* Include files:
|
|
*/
|
|
#include "config.h"
|
|
#include "common.h"
|
|
|
|
|
|
/*
|
|
* local variables.
|
|
*/
|
|
|
|
file_info **opened_files = NULL;
|
|
unsigned long opened_file_descriptors = 0;
|
|
static long page_size;
|
|
|
|
|
|
/*
|
|
* Function: void insert_line(file_info, wchar_t *, LINENUM)
|
|
*
|
|
* Description:
|
|
*
|
|
* Insert the wide character string into the file at a specific
|
|
* line.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* data -> A pointer to the wide character string to insert.
|
|
* line -> Line number in file to insert.
|
|
*/
|
|
|
|
void
|
|
insert_line(file_info *info, wchar_t *data, LINENUM line)
|
|
{
|
|
line_address *out, *in;
|
|
unsigned long i;
|
|
off_t eof;
|
|
|
|
/*
|
|
* Handle inserts beyond end of file by putting empty lines
|
|
* at the end of the file until just before the requested position.
|
|
*/
|
|
while (line > info->line_count) {
|
|
insert_line(info, L"\n", info->line_count);
|
|
}
|
|
|
|
/* Expand internal data structures if necessary */
|
|
if (info->line_count >= info->max_lines) {
|
|
info->max_lines += LINE_REALLOC_INCR;
|
|
info->lines = (line_address *)reallocate(info->lines,
|
|
sizeof (line_address) * info->max_lines);
|
|
}
|
|
|
|
/* shift up all lines above the line to insert */
|
|
if (line < info->line_count) {
|
|
out = &info->lines[info->line_count];
|
|
in = out - 1;
|
|
for (i = line; i < info->line_count; i++) {
|
|
*(out--) = *(in--);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find the end of file this is where the new text will go.
|
|
*/
|
|
if ((eof = lseek(info->tempfd, 0, SEEK_END)) == -1) {
|
|
pfatal(gettext("Temp file seek error"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* Point to it...
|
|
*/
|
|
info->lines[line].offset = eof;
|
|
info->lines[line].length = sizeof (wchar_t) * (wcslen(data) + 1);
|
|
|
|
/*
|
|
* and write out the actual data.
|
|
*/
|
|
if (write(info->tempfd, data, info->lines[line].length) == -1) {
|
|
pfatal(gettext("Temp file write error"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/* count new line */
|
|
info->line_count++;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void delete_line(info, line)
|
|
*
|
|
* Description:
|
|
*
|
|
* Delete the specified line from the file.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* line -> Line number in file to delete.
|
|
*/
|
|
|
|
void
|
|
delete_line(file_info *info, LINENUM line)
|
|
{
|
|
line_address *in, *out;
|
|
|
|
if (info->line_count > 0) {
|
|
out = &info->lines[line];
|
|
in = out + 1;
|
|
while (line++ < info->line_count) {
|
|
*(out++) = *(in++);
|
|
}
|
|
info->line_count--;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: wchar_t *fetch_line(info, line)
|
|
*
|
|
* Description:
|
|
*
|
|
* Map the specified line of the file into system memory and
|
|
* return the line's address.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* line -> Line number in file to insert.
|
|
*
|
|
* Returns:
|
|
* wchar_t * -> A pointer to the files line, NULL if line does not exist.
|
|
*/
|
|
|
|
wchar_t *
|
|
fetch_line(file_info *info, LINENUM line)
|
|
{
|
|
unsigned long new_page;
|
|
|
|
if (line < 0 || line >= info->line_count)
|
|
return (NULL);
|
|
|
|
new_page = info->lines[line].offset / page_size;
|
|
if (new_page != info->mapped_page) {
|
|
/*
|
|
if (info->mapped_page != LONG_MAX)
|
|
(void) munmap(info->mapped_address, 2 * page_size);
|
|
*/
|
|
info->mapped_address = mmap(0, 2 * page_size, PROT_READ,
|
|
MAP_PRIVATE, info->tempfd, new_page * page_size);
|
|
if (info->mapped_address == (caddr_t)-1) {
|
|
pfatal(gettext("Memory mapping error"));
|
|
/* NOTREACHED */
|
|
}
|
|
info->mapped_page = new_page;
|
|
}
|
|
return ((wchar_t *)((unsigned long)info->mapped_address +
|
|
info->lines[line].offset % page_size));
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void update_with_file_contents(info, file)
|
|
*
|
|
* Description:
|
|
*
|
|
* Remove all the lines in file and replace with the data contained in
|
|
* the file specified by file.
|
|
*
|
|
* This is used after an ed patch has been applied to get the changes
|
|
* made by ed.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* file -> Name of file that contains new data.
|
|
*/
|
|
|
|
void
|
|
update_with_file_contents(file_info *info, const char *file)
|
|
{
|
|
FILE *infd;
|
|
|
|
if ((infd = fopen(file, "r")) == NULL) {
|
|
pfatal(gettext("'%s'"), file);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/* Blow off any previous data */
|
|
(void) ftruncate(info->tempfd, 0);
|
|
info->line_count = 0;
|
|
|
|
/* We don't think any pages are mapped already */
|
|
info->mapped_page = LONG_MAX;
|
|
info->line_count = 0;
|
|
|
|
/*
|
|
* We don't need to expand lines here since the line length
|
|
* is the maximum line length of all files including the patch file.
|
|
* Any inserted lines must already exist in the patch file.
|
|
*/
|
|
while (fgetws(wbuf, max_input - 1, infd) != NULL) {
|
|
insert_line(info, wbuf, info->line_count);
|
|
}
|
|
if (ferror(infd)) {
|
|
pfatal(gettext("'%s'"), file);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
(void) fclose(infd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void cache_file(file, info)
|
|
*
|
|
* Description:
|
|
*
|
|
* Create temp file, convert all the lines in file into wide character
|
|
* format, write lines to file, and update data structures.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* file -> Name of file that contains new data.
|
|
*/
|
|
|
|
static void
|
|
cache_file(const char *file, file_info *info)
|
|
{
|
|
FILE *infd;
|
|
int characters;
|
|
|
|
if (*file != NULL) {
|
|
if ((infd = fopen(file, "r+")) == NULL) {
|
|
pfatal(gettext("'%s'"), file);
|
|
/* NOTREACHED */
|
|
}
|
|
} else {
|
|
infd = stdin;
|
|
}
|
|
|
|
info->temp_file = savestr(tmpnam(NULL));
|
|
if ((info->tempfd = open(info->temp_file,
|
|
(O_RDWR|O_CREAT|O_EXCL|O_TRUNC), 0600)) == -1) {
|
|
pfatal(gettext("Can not open temporary file"));
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
info->mapped_page = LONG_MAX;
|
|
info->mapped_address = 0;
|
|
info->line_count = 0;
|
|
while (fgetws(wbuf, max_input - 1, infd) != NULL) {
|
|
/*
|
|
* Every line should end in a newline, if this line does
|
|
* not then we assume our buffers are too small and we
|
|
* expand them to fit the new linesize.
|
|
*/
|
|
characters = wcslen(wbuf);
|
|
while (wbuf[characters - 1] != L'\n') {
|
|
max_input += BUFFER_REALLOC_SIZE;
|
|
buf = reallocate(buf, (max_input + 1) * sizeof (char));
|
|
wbuf = reallocate(wbuf,
|
|
(max_input + 1) * sizeof (wchar_t));
|
|
if (fgetws(&wbuf[characters], BUFFER_REALLOC_SIZE,
|
|
infd) == NULL)
|
|
break;
|
|
characters = wcslen(wbuf);
|
|
}
|
|
insert_line(info, wbuf, info->line_count);
|
|
}
|
|
if (ferror(infd)) {
|
|
pfatal(gettext("'%s'"), file);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
(void) fclose(infd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: int open_file(file, empty)
|
|
*
|
|
* Description:
|
|
*
|
|
* Open a file and initialize data structures.
|
|
* make an empty file if required.
|
|
*
|
|
* Inputs:
|
|
* file -> Name of file to open.
|
|
* empty -> Make the file an empty file.
|
|
*
|
|
* Returns:
|
|
* file table entry.
|
|
*/
|
|
|
|
int
|
|
open_file(char *file, int empty)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < opened_file_descriptors; i++) {
|
|
if (strcmp(file, opened_files[i]->name) == 0)
|
|
break;
|
|
}
|
|
if (i == opened_file_descriptors) {
|
|
opened_file_descriptors++;
|
|
if (opened_file_descriptors == 1) {
|
|
opened_files = allocate(sizeof (file_info *));
|
|
page_size = sysconf(_SC_PAGESIZE);
|
|
} else {
|
|
opened_files = reallocate(opened_files,
|
|
sizeof (file_info *) *
|
|
opened_file_descriptors);
|
|
}
|
|
|
|
opened_files[i] = allocate(sizeof (file_info));
|
|
opened_files[i]->name = savestr(file);
|
|
opened_files[i]->tempfd = -1;
|
|
opened_files[i]->mapped_page = LONG_MAX;
|
|
|
|
/* Allocate some initial data space too */
|
|
opened_files[i]->max_lines = LINE_REALLOC_INCR;
|
|
opened_files[i]->lines = (line_address *)
|
|
allocate(sizeof (line_address) * LINE_REALLOC_INCR);
|
|
|
|
if (empty)
|
|
cache_file("/dev/null", opened_files[i]);
|
|
else
|
|
cache_file(file, opened_files[i]);
|
|
} else {
|
|
if ((opened_files[i]->tempfd = open(opened_files[i]->temp_file,
|
|
O_RDWR|O_EXCL)) == -1) {
|
|
pfatal(gettext("Can not reopen temporary file"));
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
void
|
|
close_file(file_info *info)
|
|
{
|
|
if (info->mapped_page != LONG_MAX) {
|
|
(void) munmap(info->mapped_address, 2 * page_size);
|
|
info->mapped_page = LONG_MAX;
|
|
}
|
|
close(info->tempfd);
|
|
info->tempfd = -1;
|
|
}
|
|
|
|
/*
|
|
* Function: void sync_file(info, outname)
|
|
*
|
|
* Description:
|
|
*
|
|
* Write all the lines of a file to output file. Write to
|
|
* original file name if outfile is not specified.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* outname -> Name of output file to write to.
|
|
*/
|
|
|
|
void
|
|
sync_file(file_info *info, char *outname)
|
|
{
|
|
static int outname_opened = 0;
|
|
static FILE *out;
|
|
wchar_t *ret;
|
|
struct stat statbuf;
|
|
unsigned i;
|
|
|
|
if (lstat(info->name, &statbuf) == -1) {
|
|
statbuf.st_mode = 0666;
|
|
}
|
|
|
|
if (info->flags & SAVE_ORIGINAL) {
|
|
(void) sprintf(buf, "%s.orig", info->name);
|
|
if (rename(info->name, buf) == -1) {
|
|
pfatal(gettext("Failed to rename %s to %s"),
|
|
info->name, buf);
|
|
/* NOTREACHED */
|
|
}
|
|
info->flags &= ~SAVE_ORIGINAL;
|
|
}
|
|
|
|
if (outname == NULL) {
|
|
if ((out = fopen(info->name, "w")) == NULL) {
|
|
(void) fprintf(stderr, "patch: ");
|
|
sprintf(buf, gettext("Failed to open output file %s"),
|
|
info->name);
|
|
perror(buf);
|
|
return;
|
|
}
|
|
(void) fchmod(fileno(out), statbuf.st_mode);
|
|
} else if (*outname == NULL) {
|
|
if ((out = fopen(info->name, "w")) == NULL) {
|
|
pfatal(gettext("Failed to open output file %s"),
|
|
info->name);
|
|
/* NOTREACHED */
|
|
}
|
|
(void) fchmod(fileno(out), statbuf.st_mode);
|
|
} else if (strcmp(outname, "-") == 0) {
|
|
/* output goes to stdout */
|
|
out = stdout;
|
|
} else if (outname_opened == 0) {
|
|
if ((out = fopen(outname, "w")) == NULL) {
|
|
pfatal(gettext("Failed to open output file %s"),
|
|
outname);
|
|
/* NOTREACHED */
|
|
}
|
|
(void) fchmod(fileno(out), statbuf.st_mode);
|
|
outname_opened = 1;
|
|
}
|
|
|
|
for (i = 0; i < info->line_count; i++) {
|
|
ret = fetch_line(info, i);
|
|
(void) fputws(ret, out);
|
|
}
|
|
(void) fflush(out);
|
|
if (outname == NULL || *outname == NULL) {
|
|
(void) fclose(out);
|
|
}
|
|
}
|