home *** CD-ROM | disk | FTP | other *** search
- /* util.c - Several utility routines for cpio.
- Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
- This port is also distributed under the terms of the
- GNU General Public License as published by the
- Free Software Foundation.
-
- Please note that this file is not identical to the
- original GNU release, you should have received this
- code as patch to the official release.
-
- $Header: e:/gnu/cpio/RCS/util.c 1.1.0.2 90/09/23 23:11:12 tho Exp $
- */
-
- #include <stdio.h>
- #include <pwd.h>
- #ifndef MSDOS
- #include <grp.h>
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #ifndef MSDOS /* sigh, it's `volatile' !!! */
- extern int errno;
- #include <sys/ioctl.h>
- #endif
- #ifdef USG
- #include <string.h>
- #else
- #include <strings.h>
- #endif
- #ifndef MTIO_MISSING
- #include <sys/mtio.h>
- #endif
- #include "cpio.h"
- #include "extern.h"
-
- #ifdef MSDOS
- #include <assert.h>
- extern int mkdir (char *, int); /* we're cheating! */
- extern void fill_input_buffer (int in_des, int num_bytes);
- extern void tape_offline (int tape_des);
- extern char *xrealloc (char *ptr, unsigned int size);
- #endif /* MSDOS */
-
- /* TRUE if the last buffer written by `empty_output_buffer' was a block
- of zeros spaced forward with lseek. */
- static char last_write_made_hole;
-
- /* Write `output_size' bytes of `output_buffer' to file
- descriptor OUT_DES and reset `output_size' and `out_buff'.
- Use lseek if possible. */
-
- void
- empty_output_buffer (out_des)
- int out_des;
- {
- LONG bytes_written;
- char *cp;
- int *ip;
-
- last_write_made_hole = FALSE;
- if (output_is_seekable)
- {
- output_buffer[output_size] = 1; /* Sentinel to stop loop. */
-
- /* Find first non-zero *word*, or the word with the sentinel. */
- ip = (int *) output_buffer;
- while (*ip++ == 0)
- ;
-
- /* Find the first non-zero *byte*, or the sentinel. */
- cp = (char *) (ip - 1);
- while (*cp++ == '\0')
- ;
-
- /* If we found the sentinel, the whole input block was zero,
- and we can make a hole. */
- if (cp > &output_buffer[output_size])
- {
- last_write_made_hole = TRUE;
- if (lseek (out_des, (long) output_size, 1) < 0L)
- error (1, errno, "cannot seek on output");
- }
- }
- if (last_write_made_hole == FALSE)
- {
- #ifdef MSDOS
- assert (output_size < 0xffffL);
- bytes_written = write (out_des, output_buffer, (size_t) output_size);
- #else
- bytes_written = write (out_des, output_buffer, output_size);
- #endif
- if (bytes_written != output_size)
- {
- error (0, errno, "write error");
- if (bytes_written == 0 && output_is_special)
- {
- get_next_reel (out_des);
- #ifdef MSDOS
- bytes_written = write (out_des, output_buffer,
- (size_t) output_size);
- #else
- bytes_written = write (out_des, output_buffer, output_size);
- #endif
- }
- if (bytes_written != output_size)
- exit (1);
- }
- }
- output_bytes += output_size;
- out_buff = output_buffer;
- output_size = 0;
- }
-
- /* Read at most NUM_BYTES or `io_block_size' bytes, whichever is smaller,
- into the start of `input_buffer' from file descriptor IN_DES.
- Set `input_size' to the number of bytes read and reset `in_buff'.
- Exit with an error if end of file is reached. */
-
- void
- fill_input_buffer (in_des, num_bytes)
- int in_des;
- int num_bytes;
- {
- in_buff = input_buffer;
- num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size;
- input_size = read (in_des, input_buffer, num_bytes);
- if (input_size == 0)
- {
- error (0, 0, "premature end of file");
- if (input_is_special)
- {
- get_next_reel (in_des);
- /* Try again with a new tape. */
- input_size = read (in_des, input_buffer, num_bytes);
- }
- }
- if (input_size < 0)
- error (1, errno, "read error");
- if (input_size == 0)
- exit (1);
- input_bytes += input_size;
- }
-
- /* If the file ends with a `hole', something needs to be written at
- the end. Otherwise the kernel would truncate the file at the end
- of the last write operation. */
-
- void
- finish_output_file (path, out_des)
- char *path;
- int out_des;
- {
- if (last_write_made_hole)
- {
- /* Seek backwards one character and write a null. */
- if (lseek (out_des, (off_t) -1, 1) < 0L
- || write (out_des, "", 1) != 1)
- {
- error (0, errno, "%s", path);
- }
- }
- }
-
- /* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
- When `out_buff' fills up, flush it to file descriptor OUT_DES. */
-
- void
- copy_buf_out (in_buf, out_des, num_bytes)
- char *in_buf;
- int out_des;
- LONG num_bytes;
- {
- /* Copy the bytes one at a time, and empty the output buffer
- when it is full. */
- while (num_bytes-- > 0)
- {
- if (output_size == io_block_size)
- empty_output_buffer (out_des);
- *(out_buff++) = *(in_buf++);
- output_size++;
- }
- }
-
- /* Copy NUM_BYTES of buffer `in_buff' into IN_BUF.
- `in_buff' may be partly full.
- When `in_buff' is exhausted, refill it from file descriptor IN_DES. */
-
- void
- copy_in_buf (in_buf, in_des, num_bytes)
- char *in_buf;
- int in_des;
- LONG num_bytes;
- {
- while (num_bytes-- > 0)
- {
- if (input_size == 0)
- fill_input_buffer (in_des, io_block_size);
- *(in_buf++) = *(in_buff++);
- input_size--;
- }
- }
-
- /* Skip the next NUM_BYTES bytes of file descriptor IN_DES.
- If possible, use lseek to keep from reading all the bytes;
- otherwise (as from tapes), read all bytes. */
-
- void
- toss_input (in_des, num_bytes)
- int in_des;
- LONG num_bytes;
- {
- int num_toss; /* Number of io_block_size blocks to toss. */
- int i; /* Index for loop. */
-
- if (num_bytes > input_size)
- {
- /* We need to toss more than what is currently in the input buffer.
- Calculate the number of full blocks to toss and either seek past
- them or read them from tape. The last partial block is then read
- and prepared for a toss of less than one full block. */
- num_bytes -= input_size;
- #ifdef MSDOS
- num_toss = (int) (num_bytes / io_block_size);
- #else
- num_toss = num_bytes / io_block_size;
- #endif
- if (input_is_seekable)
- {
- /* We can seek past the blocks, saving input disk reads. */
- #ifdef MSDOS
- if (lseek (in_des, (long) num_toss * (long) io_block_size, 1) < 0L)
- #else
- if (lseek (in_des, (long) (num_toss * io_block_size), 1) < 0L)
- #endif
- error (1, errno, "cannot seek on input");
- input_bytes += num_toss * io_block_size;
- }
- else
- {
- /* The input is something that must be read serially. */
- for (i = 1; i <= num_toss; i++)
- fill_input_buffer (in_des, io_block_size);
- }
- #ifdef MSDOS
- num_bytes -= (long) num_toss * (long) io_block_size;
- #else
- num_bytes -= num_toss * io_block_size;
- #endif
- /* Get next block containing last "few" bytes to toss. */
- fill_input_buffer (in_des, io_block_size);
- }
-
- /* Adjust to correct place in the current buffer full. */
- in_buff += num_bytes;
- input_size -= num_bytes;
- }
-
- /* Copy a file using the input and output buffers, which may start out
- partly full. After the copy, the files are not closed nor the last
- block flushed to output, and the input buffer may still be partly
- full.
- IN_DES is the file descriptor for input;
- OUT_DES is the file descriptor for output;
- NUM_BYTES is the number of bytes to copy. */
-
- void
- copy_files (in_des, out_des, num_bytes)
- int in_des;
- int out_des;
- LONG num_bytes;
- {
- LONG size;
-
- while (num_bytes > 0)
- {
- if (input_size == 0)
- fill_input_buffer (in_des, io_block_size);
- size = (input_size < num_bytes) ? input_size : num_bytes;
- copy_buf_out (in_buff, out_des, size);
- num_bytes -= size;
- input_size -= size;
- in_buff += size;
- }
- }
-
- /* Create all directories up to but not including the last part of NAME.
- Do not destroy any nondirectories while creating directories. */
-
- void
- create_all_directories (name)
- char *name;
- {
- char *dir; /* Working directory name. */
- int i; /* Index into NAME. */
- int name_len; /* Length of NAME. */
- struct stat f_stat; /* File stat for directory. */
-
- name_len = strlen (name);
- for (i = name_len - 1; i >= 0 && name[i] != '/'; i--)
- /* Do nothing. */ ;
-
- /* If there are no directories in the string, just return. */
- if (i < 0)
- return;
-
- dir = copystring (name);
- dir[i + 1] = '\0';
- name_len = i + 1;
-
- /* Process the directories starting with the first one after the root:
- replace sucessive '/' with a zero byte while making sure that that
- directory exists. Then set the zero byte back to '/'. */
- for (i = 1; i < name_len; i++)
- {
- if (dir[i] == '/')
- {
- dir[i] = '\0';
- /* Use stat instead of lstat because symlinks to directories
- are just as good as directories here. */
- if (stat (dir, &f_stat) < 0)
- {
- /* File does not exist, create the directory. */
- if (mkdir (dir, 0777) < 0)
- {
- error (0, errno, "%s", dir);
- free (dir);
- return;
- }
- else if (verbose_flag)
- error (0, 0, "created directory %s", dir);
- }
- else if ((f_stat.st_mode & S_IFMT) != S_IFDIR)
- {
- error (0, 0, "%s exists but is not a directory", dir);
- free (dir);
- return;
- }
- dir[i] = '/';
- }
- }
-
- free (dir);
- }
-
- /* Return a newly allocated copy of STRING. */
-
- char *
- copystring (string)
- char *string;
- {
- return strcpy ((char *) xmalloc (strlen (string) + 1), string);
- }
-
- struct userid
- {
- int uid;
- char *name;
- struct userid *next;
- };
-
- struct userid *user_alist;
-
- /* Translate `uid' to a login name, with cache. */
-
- char *
- getuser (uid)
- int uid;
- {
- register struct userid *tail;
- struct passwd *pwent;
- char usernum_string[20];
-
- for (tail = user_alist; tail; tail = tail->next)
- if (tail->uid == uid)
- return tail->name;
-
- pwent = getpwuid (uid);
- tail = (struct userid *) xmalloc (sizeof (struct userid));
- tail->uid = uid;
- tail->next = user_alist;
- if (pwent == 0)
- {
- sprintf (usernum_string, "%d", uid);
- tail->name = copystring (usernum_string);
- }
- else
- tail->name = copystring (pwent->pw_name);
- user_alist = tail;
- return tail->name;
- }
-
- /* We use the same struct as for userids. */
- struct userid *group_alist;
-
- /* Translate `gid' to a group name, with cache. */
-
- char *
- getgroup (gid)
- int gid;
- {
- register struct userid *tail;
- struct group *grent;
- char groupnum_string[20];
-
- for (tail = group_alist; tail; tail = tail->next)
- if (tail->uid == gid)
- return tail->name;
-
- grent = getgrgid (gid);
- tail = (struct userid *) xmalloc (sizeof (struct userid));
- tail->uid = gid;
- tail->next = group_alist;
- if (grent == 0)
- {
- sprintf (groupnum_string, "%u", gid);
- tail->name = copystring (groupnum_string);
- }
- else
- tail->name = copystring (grent->gr_name);
- group_alist = tail;
- return tail->name;
- }
-
- /* Support for remembering inodes with multiple links. Used in the
- "copy in" and "copy pass" modes for making links instead of copying
- the file. */
-
- struct inode_val
- {
- unsigned inode;
- char *file_name;
- };
-
- /* Inode hash table. Allocated by first call to add_inode. */
- static struct inode_val **hash_table = NULL;
-
- /* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */
- static int hash_size = 22;
-
- /* Number of elements in current hash table. */
- static int hash_num;
-
- /* Find the file name associated with NODE_NUM. If there is no file
- associated with NODE_NUM, return NULL. */
-
- char *
- find_inode_file (node_num)
- unsigned node_num;
- {
- int start; /* Initial hash location. */
- int temp; /* Rehash search variable. */
-
- if (hash_table != NULL)
- {
- /* Hash function is node number modulo the table size. */
- start = node_num % hash_size;
-
- /* Initial look into the table. */
- if (hash_table[start] == NULL)
- return NULL;
- if (hash_table[start]->inode == node_num)
- return hash_table[start]->file_name;
-
- /* The home position is full with a different inode record.
- Do a linear search terminated by a NULL pointer. */
- for (temp = start + 1; hash_table[temp] != NULL && temp != start;
- temp = (temp + 1) % hash_size)
- {
- if (hash_table[temp]->inode == node_num)
- return hash_table[temp]->file_name;
- }
- }
-
- return NULL;
- }
-
- /* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */
-
- void
- add_inode (node_num, file_name)
- unsigned short node_num;
- char *file_name;
- {
- struct inode_val *temp;
-
- /* Create new inode record. */
- temp = (struct inode_val *) xmalloc (sizeof (struct inode_val));
- temp->inode = node_num;
- temp->file_name = copystring (file_name);
-
- /* Do we have to increase the size of (or initially allocate)
- the hash table? */
- if (hash_num == hash_size || hash_table == NULL)
- {
- struct inode_val **old_table; /* Pointer to old table. */
- int i; /* Index for re-insert loop. */
-
- /* Save old table. */
- old_table = hash_table;
- if (old_table == NULL)
- hash_num = 0;
-
- /* Calculate new size of table and allocate it.
- Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ...
- where 3197 and most of the sizes after 6397 are not prime. The other
- numbers listed are prime. */
- hash_size = 2 * hash_size + 3;
- hash_table = (struct inode_val **)
- xmalloc (hash_size * sizeof (struct inode_val *));
-
- /* Insert the values from the old table into the new table. */
- for (i = 0; i < hash_num; i++)
- hash_insert (old_table[i]);
-
- if (old_table != NULL)
- free (old_table);
- }
-
- /* Insert the new record and increment the count of elements in the
- hash table. */
- hash_insert (temp);
- hash_num++;
- }
-
- /* Do the hash insert. Used in normal inserts and resizing the hash
- table. It is guaranteed that there is room to insert the item.
- NEW_VALUE is the pointer to the previously allocated inode, file
- name association record. */
-
- void
- hash_insert (new_value)
- struct inode_val *new_value;
- {
- int start; /* Home position for the value. */
- int temp; /* Used for rehashing. */
-
- /* Hash function is node number modulo the table size. */
- start = new_value->inode % hash_size;
-
- /* Do the initial look into the table. */
- if (hash_table[start] == NULL)
- {
- hash_table[start] = new_value;
- return;
- }
-
- /* If we get to here, the home position is full with a different inode
- record. Do a linear search for the first NULL pointer and insert
- the new item there. */
- temp = (start + 1) % hash_size;
- while (hash_table[temp] != NULL)
- temp = (temp + 1) % hash_size;
-
- /* Insert at the NULL. */
- hash_table[temp] = new_value;
- }
-
- /* Attempt to rewind the tape drive on file descriptor TAPE_DES
- and take it offline. */
-
- void
- tape_offline (tape_des)
- int tape_des;
- {
- #ifndef MTIO_MISSING
- struct mtop control;
-
- control.mt_op = MTOFFL;
- control.mt_count = 1;
- ioctl (tape_des, MTIOCTOP, &control); /* Don't care if it fails. */
- #endif
- }
-
- /* The file on file descriptor TAPE_DESC is assumed to be magnetic tape
- and the end of the tape has been reached.
- Ask the user for to mount a new tape to continue the processing. */
-
- void
- get_next_reel (tape_des)
- int tape_des;
- {
- FILE *tty_in; /* File for interacting with user. */
- FILE *tty_out; /* File for interacting with user. */
- int c;
-
- /* Open files for interactive communication. */
- #ifdef MSDOS
- tty_in = fopen ("con", "r");
- if (tty_in == NULL)
- error (2, errno, "/dev/tty");
- tty_out = fopen ("con", "w");
- if (tty_out == NULL)
- error (2, errno, "/dev/tty");
- #else /* not MSDOS */
- tty_in = fopen ("/dev/tty", "r");
- if (tty_in == NULL)
- error (2, errno, "/dev/tty");
- tty_out = fopen ("/dev/tty", "w");
- if (tty_out == NULL)
- error (2, errno, "/dev/tty");
- #endif /* not MSDOS */
-
- /* Give message and wait for carrage return. User should hit carrage return
- only after loading the next tape. */
- fprintf (tty_out, "Found end of tape. Load next tape and press RETURN. ");
- fflush (tty_out);
-
- tape_offline (tape_des);
-
- do
- c = getc (tty_in);
- while (c != EOF && c != '\n');
-
- fclose (tty_in);
- fclose (tty_out);
- }
-
- /* Like malloc but get fatal error if memory is exhausted. */
-
- char *
- xmalloc (size)
- unsigned size;
- {
- char *ptr = malloc (size);
-
- if (ptr == 0)
- error (2, 0, "virtual memory exhausted");
- return ptr;
- }
-
- char *
- xrealloc (ptr, size)
- char *ptr;
- unsigned size;
- {
- ptr = realloc (ptr, size);
- if (ptr == 0)
- error (2, 0, "virtual memory exhausted");
- return ptr;
- }
-
- #ifdef MKDIR_MISSING
- /* mkdir adapted from GNU tar. */
-
- /* Make directory DPATH, with permission mode DMODE.
-
- Written by Robert Rother, Mariah Corporation, August 1985
- (sdcsvax!rmr or rmr@uscd). If you want it, it's yours.
-
- Severely hacked over by John Gilmore to make a 4.2BSD compatible
- subroutine. 11Mar86; hoptoad!gnu
-
- Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
- subroutine didn't return EEXIST. It does now. */
-
- int
- mkdir (dpath, dmode)
- char *dpath;
- int dmode;
- {
- int cpid, status;
- struct stat statbuf;
-
- if (stat (dpath, &statbuf) == 0)
- {
- errno = EEXIST; /* stat worked, so it already exists. */
- return -1;
- }
-
- /* If stat fails for a reason other than non-existence, return error. */
- if (errno != ENOENT)
- return -1;
-
- cpid = fork ();
- switch (cpid)
- {
- case -1: /* Cannot fork. */
- return -1; /* errno is set already. */
-
- case 0: /* Child process. */
- /* Cheap hack to set mode of new directory. Since this child
- process is going away anyway, we zap its umask.
- This won't suffice to set SUID, SGID, etc. on this
- directory, so the parent process calls chmod afterward. */
- status = umask (0); /* Get current umask. */
- umask (status | (0777 & ~dmode)); /* Set for mkdir. */
- execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
- _exit (1);
-
- default: /* Parent process. */
- while (wait (&status) != cpid) /* Wait for kid to finish. */
- /* Do nothing. */ ;
-
- if (status & 0xFFFF)
- {
- errno = EIO; /* /bin/mkdir failed. */
- return -1;
- }
- return chmod (dpath, dmode);
- }
- }
- #endif
-
- #ifdef BCOPY_MISSING
- /* Copy COUNT bytes from SOURCE to DEST, handling overlaps properly.
- Do not null-terminate. */
-
- void
- bcopy (source, dest, count)
- char *source, *dest;
- unsigned count;
- {
- if (source < dest)
- /* Moving from low to high memory; start at end. */
- for (source += count, dest += count; count > 0; --count)
- *--dest = *--source;
- else if (source != dest)
- /* Moving from high to low memory; start at beginning. */
- for (; count > 0; --count)
- *dest++ = *source++;
- }
- #endif
-