Newsgroups: comp.sources.unix From: ejb@ERA.COM (Jay Berkenbilt) Subject: v28i005: bcs-2.0 - A Baseline Configuration System, Part04/25 References: <1.764985670.1461@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: ejb@ERA.COM (Jay Berkenbilt) Posting-Number: Volume 28, Issue 5 Archive-Name: bcs-2.0/part04 #!/bin/sh # this is bcs.04 (part 4 of bcs-2.0) # do not concatenate these parts, unpack them in order with /bin/sh # file bcs-2.0/lib/sccs_cm.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 4; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping bcs-2.0/lib/sccs_cm.c' else echo 'x - continuing file bcs-2.0/lib/sccs_cm.c' sed 's/^X//' << 'SHAR_EOF' >> 'bcs-2.0/lib/sccs_cm.c' && { X char logfile[MAXPATHLEN]; X char lastcomp[MAXPATHLEN]; X char *p; X X strcpy(logfile, filename); X if ((p = strrchr(logfile, PATH_SEP_CHAR)) == NULL) X { X strcpy(lastcomp, filename); X p = logfile; X } X else X { X p++; X strcpy(lastcomp, p); X } X X strcpy(p, SCCS_DIR); X strcat(p, PATH_SEP_STR); X strcat(p, "s."); X strcat(p, lastcomp); X X return (bcsi_exists(bcs_obj, logfile)); } X static int sccs_locked(bcs_obj_type bcs_obj, char *filename) { X char lockfile[MAXPATHLEN]; X char lastcomp[MAXPATHLEN]; X char *p; X X strcpy(lockfile, filename); X if ((p = strrchr(lockfile, PATH_SEP_CHAR)) == NULL) X { X strcpy(lastcomp, filename); X p = lockfile; X } X else X { X p++; X strcpy(lastcomp, p); X } X X strcpy(p, SCCS_DIR); X strcat(p, PATH_SEP_STR); X strcat(p, "p."); X strcat(p, lastcomp); X X return (bcsi_exists(bcs_obj, lockfile)); } X static int sccs_uptodate(bcs_obj_type bcs_obj, char *filename) { X char command[2 * MAXPATHLEN]; X char tmppath[MAXPATHLEN]; X char *contdir; X char *lastcomp; X X strcpy(tmppath, filename); X if ((lastcomp = strrchr(tmppath, PATH_SEP_CHAR)) == NULL) X { X lastcomp = tmppath; X contdir = ""; X } X else X { X *lastcomp = '\0'; X lastcomp++; X contdir = tmppath; X } X X sprintf(command, X "/bin/sh -c \"cd %s; %s get -p %s 2>/dev/null | " X "%s - %s > /dev/null 2>&1\"", X contdir, SCCS_PATH, lastcomp, DIFF_PATH, lastcomp); X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_uptodate: running %s", X command)); X return (system(command) == 0); } X static int sccs_update_bl(bcs_obj_type bcs_obj, bcs_file_info file_info) { X char command[2 * MAXPATHLEN]; X char tmpname[MAXPATHLEN]; X char *name; X X if (! fi_bl_info(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stderr, X "sccs_update_bl called with file_info with no bl_info"); X abort(); X } X X name = fi_clean_path(bcs_obj, fi_bl_info(bcs_obj, file_info)); X X strcpy(tmpname, name); X strcat(tmpname, ",bcs"); X sprintf(command, "sh -c \"%s get -p %s > %s\"", SCCS_PATH, name, tmpname); X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_update_bl: running %s", X command)); X if (system(command) != 0) X return FALSE; X X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_update_bl: removing %s", X name)); X if (unlink(name) == -1) X return FALSE; X X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_update_bl: renaming %s to %s", X tmpname, name)); X if (rename(tmpname, name) == -1) X return FALSE; X X (void) chmod(name, 0444); X return TRUE; } X static int sccs_register_file(bcs_obj_type bcs_obj, char *bl_filename, X char *st_filename) { X char workpath[MAXPATHLEN]; X char dirpath[MAXPATHLEN]; X char contdir[MAXPATHLEN]; X char *p; X int status; X char cmd[2 * MAXPATHLEN]; X bcs_file_info file_info; X X strcpy(workpath, bl_filename); X strcpy(dirpath, bl_filename); X X /* Make the SCCS directory if it doesn't exist */ X if ((p = strrchr(dirpath, PATH_SEP_CHAR)) == NULL) X p = dirpath; X else X p++; X X /* Get path of directory containing this file */ X mem_zero(contdir, sizeof(contdir)); X if (p == dirpath) X strcpy(contdir, "."); X else X strncpy(contdir, dirpath, (p - dirpath - 1)); X X strcpy(p, SCCS_DIR); X X if (! bcsi_exists(bcs_obj, dirpath)) X { X if (mkdir(dirpath, 0777) == -1) X { X bcs_message(bcs_obj, stderr, "mkdir %s failed: %s", X dirpath, error_string(errno)); X return BCS_ERR_SYSTEM; X } X } X X /* Create the file locked initially */ X if (! bcsi_exists(bcs_obj, bl_filename)) X { X if ((status = bcsi_create_empty_file(bcs_obj, bl_filename, 0444))) X return status; X } X sprintf(cmd, "sh -c \"cd %s; %s create %s\"", contdir, SCCS_PATH, X bl_filename); X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_register_file: running %s", X cmd)); X if (system(cmd) != 0) X return BCS_ERR_SYSTEM; X /* It doesn't really matter whether this fails... */ X (void) chmod(bl_filename, 0444); X X /* Lock the file */ X sprintf(cmd, "sh -c \"cd %s; %s edit -p %s > /dev/null 2>&1\"", contdir, X SCCS_PATH, bl_filename); X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "sccs_register_file: running %s", X cmd)); X if (system(cmd) != 0) X return BCS_ERR_SYSTEM; X X /* Make sure the SCCS directory exists in the staging area */ X file_info = bcs_get_file_info(bcs_obj, dirpath); X if (file_info == NULL) X { X bcs_message(bcs_obj, stderr, X "can't get file information for %s which we just " X "checked for!", dirpath); X return BCS_ERR_SYSTEM; X } X X bcsi_concat_paths X (workpath, X bcs_obj->staging->data[fi_index(bcs_obj, file_info)]->path.user_path, X fi_rel_path(bcs_obj, file_info)); X X status = bcsi_sync_file(bcs_obj, file_info, FALSE); X return status; } X /* X * This routine takes a set of arguments and returns a stringlist X * containing those that are filenames. The stringlist points into X * the argv array. X */ bcs_stringlist sccs_get_file_args(bcs_obj_type bcs_obj, char *argv[]) { X char **arg; X bcs_stringlist list; X X list = (bcs_stringlist) fl_new_flex_list(bcs_obj->whoami); X X for (arg = argv; *arg; arg++) X { X if ((*arg)[0] != '-') X { X fl_add_to_tail(bcs_obj->whoami, (flex_list) list, *arg); X } X } X X return list; } X /* X * PUBLIC FUNCTIONS X */ X void cm_register_sccs(bcs_obj_type bcs_obj) { X cm_command *cmd; X char tmppath[MAXPATHLEN]; X char *p; X X strcpy(tmppath, SCCS_PATH); X strcat(tmppath, " "); X p = tmppath + strlen(tmppath); X X for (cmd = sccs_cmds; cmd->name; cmd++) X { X cmd->get_file_args = sccs_get_file_args; X strcpy(p, cmd->name); X cmd->fullcmd = bcsi_safe_strdup(bcs_obj->whoami, tmppath); X } X X sccs_cm.name = "SCCS"; X sccs_cm.cmds = sccs_cmds; X sccs_cm.precious_files = sccs_precious; X sccs_cm.haslog = sccs_haslog; X sccs_cm.locked = sccs_locked; X sccs_cm.uptodate = sccs_uptodate; X sccs_cm.update_bl = sccs_update_bl; X sccs_cm.register_file = sccs_register_file; X X cm_register(bcs_obj, &sccs_cm); } X #else /* HAVE_SCCS */ X void cm_register_sccs(bcs_obj_type bcs_obj) { X return; } X #endif /* HAVE_SCCS */ SHAR_EOF echo 'File bcs-2.0/lib/sccs_cm.c is complete' && chmod 0444 bcs-2.0/lib/sccs_cm.c || echo 'restore of bcs-2.0/lib/sccs_cm.c failed' Wc_c="`wc -c < 'bcs-2.0/lib/sccs_cm.c'`" test 7789 -eq "$Wc_c" || echo 'bcs-2.0/lib/sccs_cm.c: original size 7789, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= bcs-2.0/lib/sccs_cm.h ============== if test -f 'bcs-2.0/lib/sccs_cm.h' -a X"$1" != X"-c"; then echo 'x - skipping bcs-2.0/lib/sccs_cm.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting bcs-2.0/lib/sccs_cm.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'bcs-2.0/lib/sccs_cm.h' && /* X * $Id: sccs_cm.h,v 1.3 1994/03/07 21:47:41 qjb Exp $ X * $Source: /local/tmp/bcs-2.0/lib/RCS/sccs_cm.h,v $ X * $Author: qjb $ X * X * Copyright (C) 1994 E. Jay Berkenbilt X * X * This file is part of BCS. BCS may be distributed according to the terms X * of the General Public License and/or the Artistic License. See the X * `COPYING' and `Artistic' files with the source distribution for details. X * This notice must be kept intact when this file is distributed. X * X * This file defines parameters for SCCS support X */ X #ifndef __BCS_SCCS_CM_H__ #define __BCS_SCCS_CM_H__ X #if !defined(lint) && !defined(CODECENTER) || defined(RCS_HDRS) /* Define a static function and call it. No warnings this way. */ static void rcsid_bcs_sccs_cm_h(char *s) {rcsid_bcs_sccs_cm_h("@(#)$Id: sccs_cm.h,v 1.3 1994/03/07 21:47:41 qjb Exp $");} #endif /* !lint && !CODECENTER || RCS_HDRS */ X #include "sccs_paths.h" #define SCCS_DIR "SCCS" X X #endif /* __BCS_SCCS_CM_H__ */ SHAR_EOF chmod 0444 bcs-2.0/lib/sccs_cm.h || echo 'restore of bcs-2.0/lib/sccs_cm.h failed' Wc_c="`wc -c < 'bcs-2.0/lib/sccs_cm.h'`" test 958 -eq "$Wc_c" || echo 'bcs-2.0/lib/sccs_cm.h: original size 958, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= bcs-2.0/lib/staging.c ============== if test -f 'bcs-2.0/lib/staging.c' -a X"$1" != X"-c"; then echo 'x - skipping bcs-2.0/lib/staging.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting bcs-2.0/lib/staging.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'bcs-2.0/lib/staging.c' && /* X * $Id: staging.c,v 1.34 1994/03/07 21:47:41 qjb Exp $ X * $Source: /local/tmp/bcs-2.0/lib/RCS/staging.c,v $ X * $Author: qjb $ X * X * Copyright (C) 1994 E. Jay Berkenbilt X * X * This file is part of BCS. BCS may be distributed according to the terms X * of the General Public License and/or the Artistic License. See the X * `COPYING' and `Artistic' files with the source distribution for details. X * This notice must be kept intact when this file is distributed. X * X * This file implements routines concerned with staging and unstaging X * files and directories. X */ X #if !defined(lint) && !defined(CODECENTER) || defined(RCS_HDRS) /* Define a static function and call it. No warnings this way. */ static void rcsid(char *s) {rcsid("@(#)$Id: staging.c,v 1.34 1994/03/07 21:47:41 qjb Exp $");} #endif /* !lint && !CODECENTER || RCS_HDRS */ X #include "bcs_p.h" #include X /* For examine_dir callback data */ typedef struct { X int verbose; X int recursive; X int status; X int return_status; } sync_staging_args; X /* For examine_dir callback data */ typedef struct { X int answer; } unstage_args; X static int stage_directly(bcs_obj_type, char *, bcs_file_info, int); void check_each_unstage(bcs_obj_type, bcs_file_info, void *); X /* X * PRIVATE ROUTINES X */ X /* Callback for examine_dir */ void sync_each_dir(bcs_obj_type bcs_obj, bcs_file_info file_info, X void *cbdata) { X char tmppath[MAXPATHLEN]; X sync_staging_args *sargs = (sync_staging_args *)cbdata; X X if (fi_isdir(bcs_obj, file_info)) X { X strcpy(tmppath, fi_user_path(bcs_obj, file_info)); X if ((sargs->status = bcs_sync_staging(bcs_obj, tmppath, X sargs->recursive, X sargs->verbose))) X sargs->return_status = sargs->status; X } } X /* Callback for examine_dir */ void sync_each_file(bcs_obj_type bcs_obj, bcs_file_info file_info, X void *cbdata) { X sync_staging_args *sargs = (sync_staging_args *)cbdata; X X if (fi_important(bcs_obj, file_info) && X (fi_stage_params(bcs_obj, file_info) != fi_no_mirror)) X { X if ((sargs->status = bcsi_sync_file(bcs_obj, file_info, X sargs->verbose))) X sargs->return_status = sargs->status; X } } X /* Callback for examine_dir */ void prune_links(bcs_obj_type bcs_obj, bcs_file_info file_info, X void *cbdata) { X sync_staging_args *sargs = (sync_staging_args *)cbdata; X int save_link = FALSE; X X if ((fi_link_type(bcs_obj, file_info) != fi_not_link) && X (! fi_important(bcs_obj, file_info))) X { X if (fi_link_type(bcs_obj, file_info) == fi_internal) X { X bcs_file_info link_info; X link_info = fi_ult_link_info(bcs_obj, file_info); X if (! (link_info && X (fi_which(bcs_obj, link_info) == in_staging))) X { X bcs_message(bcs_obj, stderr, X "INTERNAL ERROR (sync_staging 2): " X "%s has incorrect link type (%d)", X fi_user_path(bcs_obj, file_info), X fi_link_type(bcs_obj, file_info)); X abort(); X } X if (! fi_important(bcs_obj, link_info)) X save_link = TRUE; X } X X if (! save_link) X { X char tmppath[MAXPATHLEN]; X X strcpy(tmppath, fi_user_path(bcs_obj, file_info)); X if (sargs->verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, X "removing unimportant link %s", X tmppath); X if ((sargs->status = bcsi_remove_file(bcs_obj, tmppath))) X sargs->return_status = sargs->status; X } X } X X /* X * Warn for unimportant files with specified suffixes. Don't X * worry about directories; they are picked up elsewhere. X * directories. X */ X if ((fi_link_type(bcs_obj, file_info) == fi_not_link) && X (! fi_important(bcs_obj, file_info)) && X (! fi_isdir(bcs_obj, file_info)) && X (fi_unimp_warn(bcs_obj, file_info))) X { X bcs_message(bcs_obj, stdout, "WARNING: found unimportant %s %s", X fi_isdir(bcs_obj, file_info) ? "directory" : "file", X fi_user_path(bcs_obj, file_info)); X } } X /* X * Synchronize staging area directory dir. dir must be in the X * staging area. X */ static int bcsi_sync_staging_dir(bcs_obj_type bcs_obj, char *dir, int verbose) { X int status = BCS_SUCCESS; X int return_status = BCS_SUCCESS; X bcs_file_info file_info; X char bl_path[MAXPATHLEN]; X sync_staging_args sargs; X X sargs.verbose = verbose; X sargs.status = status; X sargs.return_status = return_status; X X file_info = bcs_get_file_info(bcs_obj, dir); X if (file_info == NULL) X /* Nothing to do */ X return BCS_SUCCESS; X X if (fi_which(bcs_obj, file_info) != in_staging) X { X bcs_message(bcs_obj, stderr, X "INTERNAL ERROR: sync_staging called with " X "dir (%s) not in staging area", dir); X abort(); X } X X if (! fi_isdir(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stderr, X "INTERNAL ERROR: sync_staging_dir called with " X "dir (%s) not a directory", dir); X abort(); X } X X if (! fi_has_baseline_counterpart(bcs_obj, file_info)) X { X /* This is not an error, but a warning is warranted. */ X bcs_message(bcs_obj, stdout, X "WARNING: %s does not exist in the baseline", X fi_user_path(bcs_obj, file_info)); X return BCS_SUCCESS; X } X X /* X * If we've made it this far, dir is a staging area directory X * with a baseline counterpart. X */ X X /* Examine the baseline counterpart */ X /* Construct path to baseline counterpart */ X bcsi_concat_paths(bl_path, X bcs_obj->baseline->data X [fi_index(bcs_obj, file_info)]->path.user_path, X fi_rel_path(bcs_obj, file_info)); X X if ((status = bcs_examine_dir(bcs_obj, bl_path, FALSE, sync_each_file, X &sargs)) X != BCS_SUCCESS) X return status; X X if ((status = bcs_examine_dir(bcs_obj, dir, FALSE, prune_links, &sargs)) X != BCS_SUCCESS) X return status; X X return_status = sargs.return_status; X X return return_status; } X static int stage_directly(bcs_obj_type bcs_obj, char *filename, X bcs_file_info bl_info, int verbose) { X int status = BCS_SUCCESS; X X if (verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, "staging %s", filename); X X if (! bcs_obj->no_action) X { X if (fi_isdir(bcs_obj, bl_info)) X { X if ((status = bcsi_make_directory(bcs_obj, filename)) == X BCS_SUCCESS) X { X status = bcsi_sync_staging_dir(bcs_obj, filename, verbose); X } X } X else X { X status = bcsi_copy_file(bcs_obj, fi_can_path(bcs_obj, bl_info), X filename); X /* X * If this file has a log, try to make sure the copy is X * read-only. This way, if the file is read/write in the X * baseline, it will still be read-only in the staging area. X * Don't worry if this fails though. X */ X if (cm_haslog(bcs_obj, filename)) X (void) chmod(filename, 0444); X } X } X X if (status == BCS_SUCCESS) X /* print this message even if not verbose */ X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "staged %s", filename); X X return status; } X static int unstage_eligible(bcs_obj_type bcs_obj, char *filename, X bcs_file_info file_info, int silent) { X bcs_file_info cwd_info = NULL; X X if (file_info == NULL) X { X if (! silent) X bcs_message(bcs_obj, stderr, X "can't unstage %s because it can't be found.", X filename); X return FALSE; X } X X if (fi_which(bcs_obj, file_info) != in_staging) X { X if (! silent) X bcs_message(bcs_obj, stderr, X "can't unstage %s because it is not in the " X "staging area.", filename); X return FALSE; X } X X if (! fi_is_staged(bcs_obj, file_info)) X { X if (! silent) X bcs_message(bcs_obj, stderr, X "can't unstage %s because it is not staged.", X filename); X return FALSE; X } X X if ((cwd_info = bcs_get_file_info(bcs_obj, ".")) != NULL) X { X char *cwd_path; X char *staging_path; X X cwd_path = fi_can_path(bcs_obj, cwd_info); X staging_path = fi_can_path(bcs_obj, file_info); X if (((strlen(staging_path) == strlen(cwd_path)) && X (strcmp(staging_path, cwd_path) == 0)) || X ((strlen(staging_path) < strlen(cwd_path)) && X (strncmp(staging_path, cwd_path, strlen(staging_path)) == 0) && X (cwd_path[strlen(staging_path)] == PATH_SEP_CHAR))) X { X bcs_message(bcs_obj, stderr, X "can't unstage %s because it would cause removal of " X "current directory.", filename); X return FALSE; X } X } X else X { X bcs_message(bcs_obj, stderr, X "can't get information about current directory."); X return FALSE; X } X X X return TRUE; } X X static int confirm_unstage_file(bcs_obj_type bcs_obj, char *filename, X bcs_file_info file_info, X unstage_param_type unstage_params) { X if (! cm_haslog(bcs_obj, filename)) X return TRUE; X X /* X * This file has a log. Make sure that it is not writable and that X * it is up-to-date in the baseline. X */ X X if (fi_writable(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stdout, "%s is writable.", filename); X } X else if (! cm_uptodate(bcs_obj, X fi_clean_path(bcs_obj, X fi_bl_info(bcs_obj, file_info)))) X { X bcs_message(bcs_obj, stdout, X "%s does not appear to be updated in the baseline; " X "changes may be hidden.", filename); X } X else X return TRUE; X X if (unstage_params == us_negative) X return FALSE; X else X return bcsi_yn("Unstage anyway?"); } X X static int confirm_unstage(bcs_obj_type bcs_obj, char *filename, X bcs_file_info file_info, X unstage_param_type unstage_params) { X unstage_args uargs; X X uargs.answer = TRUE; X X if (unstage_params == us_affirmative) X return TRUE; X X if (bcs_isdir(bcs_obj, filename)) X { X if (bcs_examine_dir(bcs_obj, filename, FALSE, check_each_unstage, X &uargs)) X return FALSE; X X if ((! uargs.answer) && (unstage_params == us_ask)) X { X char message[2 * MAXPATHLEN]; X sprintf(message, "Unstage %s anyway?", filename); X uargs.answer = bcsi_yn(message); X } X } X else X { X uargs.answer = confirm_unstage_file(bcs_obj, filename, file_info, X unstage_params); X } X X return uargs.answer; } X /* Callback for examine_dir */ void check_each_unstage(bcs_obj_type bcs_obj, bcs_file_info cur_file_info, X void *cbdata) { X unstage_args *uargs = (unstage_args *)cbdata; X X if ((unstage_eligible(bcs_obj, X fi_user_path(bcs_obj, cur_file_info), X cur_file_info, TRUE)) && X (! confirm_unstage(bcs_obj, X fi_user_path(bcs_obj, cur_file_info), X cur_file_info, us_negative))) X { X uargs->answer = FALSE; X } X X if ((! fi_important(bcs_obj, cur_file_info)) && X (fi_unimp_warn(bcs_obj, cur_file_info))) X { X bcs_message(bcs_obj, stdout, "%s %s is unimportant.", X fi_isdir(bcs_obj, cur_file_info) ? "directory" : "file", X fi_user_path(bcs_obj, cur_file_info)); X uargs->answer = FALSE; X } } X X /* X * PUBLIC ROUTINES X */ X int bcsi_sync_file(bcs_obj_type bcs_obj, bcs_file_info bl_info, int verbose) { X int status = BCS_SUCCESS; X int return_status = BCS_SUCCESS; X bcs_file_info st_info; X char st_path[MAXPATHLEN]; X enum { update, remove, create, none } action; X char destpath[MAXPATHLEN]; X char *p; X X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, X "bcsi_sync_file: checking %s", X fi_user_path(bcs_obj, bl_info))); X /* Get path to staging area counterpart */ X bcsi_concat_paths(st_path, X bcs_obj->staging->data X [fi_index(bcs_obj, bl_info)]->path.user_path, X fi_rel_path(bcs_obj, bl_info)); X st_info = bcs_get_file_info(bcs_obj, st_path); X if (st_info) X strcpy(st_path, fi_user_path(bcs_obj, st_info)); X X if (fi_link_type(bcs_obj, bl_info) == fi_not_link) X { X /* If the file in the baseline is a file... */ X if (st_info == NULL) X { X /* Create staging area counterpart if it doesn't exist */ X action = create; X } X else if ((fi_link_type(bcs_obj, st_info) == fi_not_link) || X ((fi_link_type(bcs_obj, st_info) == fi_to_baseline) && X (fi_stage_params(bcs_obj, bl_info) != fi_auto_stage))) X { X /* Do nothing */ X action = none; X if (fi_is_staged(bcs_obj, st_info) && X fi_stage_params(bcs_obj, bl_info) == fi_no_stage) X { X bcs_message(bcs_obj, stdout, X "WARNING: %s is staged but is marked unstageable", X st_path); X } X } X else X { X action = update; X } X X if (action != none) X { X /* X * Since baseline file is a file, the linkpath of the X * staging area counterpart should be to its baseline X * counterpart. X */ X p = bcsi_calc_bl_path(bcs_obj, fi_index(bcs_obj, bl_info), X fi_rel_path(bcs_obj, bl_info)); X strcpy(destpath, p); X free(p); X } X } X else X { X if (st_info == NULL) X { X /* Create staging area counterpart if it doesn't exist */ X action = create; X } X else if (fi_link_type(bcs_obj, st_info) == fi_not_link) X { X /* Warn since this file should not exist */ X action = none; X bcs_message(bcs_obj, stdout, X "WARNING: %s is a file but should be a link", X st_path); X } X else X { X action = update; X } X X if (action != none) X { X /* X * Calculate destination and determine whether action X * is really necessary X */ X if (fi_link_type(bcs_obj, bl_info) == fi_external) X { X /* X * If baseline file is an external link, X * staging area file should point to baseline X * counterpart. X */ X char *tmp; X tmp = bcsi_calc_bl_path(bcs_obj, fi_index(bcs_obj, bl_info), X fi_rel_path(bcs_obj, bl_info)); X if (tmp) X { X strcpy(destpath, tmp); X free(tmp); X } X } X else if (fi_link_type(bcs_obj, bl_info) == fi_internal) X { X /* X * If baseline file points within baseline, adjust X * the absolute path so that it points to the X * staging area. Never copy relative paths since X * they may not point to the right thing for baselines X * and staging areas that span multiple file systems. X */ X bcs_file_info link_info; X link_info = fi_ult_link_info(bcs_obj, bl_info); X if (! (link_info && X (fi_which(bcs_obj, link_info) == in_baseline))) X { X bcs_message(bcs_obj, stderr, "INTERNAL ERROR " X "(sync_staging 1): " X "%s has incorrect link type (%d)", X fi_user_path(bcs_obj, bl_info), X fi_link_type(bcs_obj, bl_info)); X abort(); X } X bcsi_concat_paths X (destpath, X bcs_obj->staging->data[fi_index(bcs_obj, link_info)]-> X path.user_path, fi_rel_path(bcs_obj, link_info)); X } X else if (fi_link_type(bcs_obj, bl_info) == fi_dangling) X { X if (action == update) X action = remove; X else X action = none; X } X } X X if ((action == update) && X (strcmp(destpath, fi_linkpath(bcs_obj, st_info)) == 0)) X action = none; X } X X X switch (action) X { X case none: X break; X X case update: X case create: X if (action == update) X { X if (verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, X "removing out-of-date link for %s", st_path); X if ((status = bcsi_remove_file(bcs_obj, st_path))) X return_status = status; X } X X if (fi_stage_params(bcs_obj, bl_info) == fi_auto_stage) X { X /* X * No need for message if calling stage verbosely since X * stage_directly prints one. X * For now, we're passing verbose = FALSE, though... X */ X if (verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, "staging %s", st_path); X if ((status = stage_directly(bcs_obj, st_path, bl_info, FALSE))) X return_status = status; X } X else X { X if (verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, "linking %s", st_path); X if ((status = bcsi_make_link(bcs_obj, destpath, st_path))) X return_status = status; X } X break; X X case remove: X if (verbose && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, "removing superfluous link %s", st_path); X if ((status = bcsi_remove_file(bcs_obj, st_path))) X return_status = status; X break; X X default: X bcs_message(bcs_obj, stderr, X "INTERNAL ERROR: default reached in action " X "in bcsi_sync_file"); X abort(); X } X X return return_status; } X /* X * Return true if this file is eligible to be staged. X */ int bcsi_stageable(bcs_obj_type bcs_obj, char *pathname, X bcs_stringlist *prereqp) { X bcs_file_info file_info; X bcs_file_info bl_info; X enum { no, yes, dont_know } result = dont_know; X char workpath[MAXPATHLEN]; X char *p; X bcs_stringlist prereq = NULL; X bcs_which orig_which = in_none; X X file_info = bcs_get_file_info(bcs_obj, pathname); X if (file_info != NULL) X orig_which = fi_which(bcs_obj, file_info); X X if (prereqp) X { X prereq = (bcs_stringlist) fl_new_flex_list(bcs_obj->whoami); X *prereqp = prereq; X } X X strcpy(workpath, pathname); X X do { X file_info = bcs_get_file_info(bcs_obj, workpath); X if ((file_info == NULL) || X (fi_which(bcs_obj, file_info) == in_none)) X { X if (orig_which == in_baseline) X bcs_message(bcs_obj, stderr, X "can't stage %s since it is in the baseline.", X pathname); X else X bcs_message(bcs_obj, stderr, X "can't stage %s since it is not in the " X "staging area.", pathname); X result = no; X } X else X { X if (fi_which(bcs_obj, file_info) == in_staging) X { X if (fi_link_type(bcs_obj, file_info) != fi_to_baseline) X { X bcs_message(bcs_obj, stderr, X "can't stage %s since it is not a link to its " X "baseline counterpart.", pathname); X result = no; X } X else X { X bl_info = fi_bl_info(bcs_obj, file_info); X if ((fi_stage_params(bcs_obj, bl_info) == fi_no_mirror) || X (fi_stage_params(bcs_obj, bl_info) == fi_no_stage)) X { X bcs_message(bcs_obj, stderr, X "%s is forbidden from being staged.", X pathname); X result = no; X } X else if (fi_link_type(bcs_obj, bl_info) != fi_not_link) X { X bcs_message(bcs_obj, stderr, X "can't stage %s since it is a link " X "to a link.", pathname); X result = no; X } X else if (! fi_important(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stderr, X "can't stage %s since it is not" X " important.", pathname); X result = no; X } X else X { X if (prereq) X fl_add_to_tail(bcs_obj->whoami, (flex_list) prereq, X bcsi_safe_strdup(bcs_obj->whoami, X workpath)); X result = yes; X } X } X } X else X { X /* File is in the baseline */ X if (! ((fi_link_type(bcs_obj, file_info) == fi_not_link) && X ((fi_stage_params(bcs_obj, file_info) == X fi_normal) || X (fi_stage_params(bcs_obj, file_info) == X fi_auto_stage)))) X { X bcs_message(bcs_obj, stderr, X "%s is not eligible to be staged.", X pathname); X result = no; X } X else X { X if (prereq) X fl_add_to_tail(bcs_obj->whoami, (flex_list) prereq, X bcsi_safe_strdup(bcs_obj->whoami, X workpath)); X if ((p = strrchr(workpath, PATH_SEP_CHAR))) X *p = '\0'; X else X { X bcs_message(bcs_obj, stderr, X "can't stage %s since it is not in the " X "staging area.", pathname); X result = no; X } X } X } X } X } while (result == dont_know); X X if ((result == no) && prereqp) X fl_delete_flex_list((flex_list *)prereqp, free); X X return (result == yes) ? TRUE : FALSE; } X int bcs_sync_staging(bcs_obj_type bcs_obj, char *dir, int recursive, X int verbose) { X int return_status = BCS_SUCCESS; X int status = BCS_SUCCESS; X sync_staging_args sargs; X X sargs.verbose = verbose; X sargs.recursive = recursive; X sargs.status = status; X sargs.return_status = return_status; X X if ((status = bcsi_sync_staging_dir(bcs_obj, dir, verbose))) X return status; X X if (recursive) X { X if ((status = bcs_examine_dir(bcs_obj, dir, FALSE, sync_each_dir, X &sargs))) X return status; X X return_status = sargs.return_status; X } X X return return_status; } X int bcs_stage(bcs_obj_type bcs_obj, char *filename, int verbose) { X int status = BCS_SUCCESS; X bcs_file_info file_info; X bcs_stringlist prereq = NULL; X int i; X X file_info = bcs_get_file_info(bcs_obj, filename); X if (file_info == NULL) X { X bcs_message(bcs_obj, stderr, X "can't stage %s because it does not exist", filename); X return BCS_ERR_USER; X } X else if (fi_is_staged(bcs_obj, file_info)) X { X /* This is not an error condition */ X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "%s is already staged.", filename); X return BCS_SUCCESS; X } X else X { X if (bcsi_stageable(bcs_obj, filename, &prereq)) X { X status = BCS_SUCCESS; X for (i = prereq->n - 1; (i >= 0) && (! status); i--) X { X if (! bcs_obj->no_action) X { X /* X * Retrieve file information for baseline counterpart. X * This must be done before removing the file! X */ X file_info = bcs_get_file_info(bcs_obj, prereq->data[i]); X if (file_info != NULL) X { X /* X * Make sure this wasn't already staged. This could X * happen If some of the prerequisites are autostage... X */ X if (! fi_is_staged(bcs_obj, file_info)) X { X if (file_info == NULL) X { X bcs_message(bcs_obj, stderr, X "INTERNAL ERROR: Can't get file " X "information for a stage " X "prerequisite (%s)", X prereq->data[i]); X return BCS_ERR_INTERNAL; X } X file_info = fi_bl_info(bcs_obj, file_info); X bcsi_remove_file(bcs_obj, prereq->data[i]); X status = stage_directly(bcs_obj, prereq->data[i], X file_info, verbose); X } X else X { X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "staged %s", X prereq->data[i]); X } X } X else X { X /* X * This file no longer exists. This could happen X * if someone goes to stage an unimportant file X * whose parent directory needed to be staged. X */ X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, X "can't stage %s because it " X "does not exist", X prereq->data[i]); X } X } X else X { X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, X "staged %s", prereq->data[i]); X } X } X fl_delete_flex_list((flex_list *)&prereq, (fl_free_fn_type)free); X } X else X status = BCS_ERR_USER; X } X X return status; } X X int bcs_unstage(bcs_obj_type bcs_obj, char *filename, X unstage_param_type unstage_params) { X int status = BCS_SUCCESS; X bcs_file_info file_info; X X file_info = bcs_get_file_info(bcs_obj, filename); X X if (file_info && (fi_link_type(bcs_obj, file_info) == fi_to_baseline)) X { X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "%s is already unstaged", filename); X return BCS_SUCCESS; X } X X if (! unstage_eligible(bcs_obj, filename, file_info, FALSE)) X return BCS_ERR_USER; X X if (confirm_unstage(bcs_obj, filename, file_info, unstage_params)) X { X int restage; X X /* Perform the unstage operation */ X restage = (fi_stage_params(bcs_obj, fi_bl_info(bcs_obj, file_info)) == X fi_auto_stage); X /* X * Retrieve file information for baseline counterpart. X * This must be done before removing the file! X */ X file_info = fi_bl_info(bcs_obj, file_info); X if (! bcs_obj->no_action) X { X if ((status = bcsi_remove_file(bcs_obj, filename)) != BCS_SUCCESS) X return status; X } X if (restage && (! bcs_obj->silent)) X bcs_message(bcs_obj, stdout, "removed %s", filename); X if (! bcs_obj->no_action) X status = bcsi_sync_file(bcs_obj, file_info, FALSE); X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "unstaged%s %s", X restage ? " and restaged" : "", filename); X } X else X { X bcs_message(bcs_obj, stdout, "NOT unstaging %s", filename); X status = BCS_ERR_USER; X } X X return status; } X /* X * Update the baseline copy of this file. This file must be in the X * staging area, have a baseline counterpart, and have a CM log X * to be eligible for updating. It may be staged or unstaged. If X * it is staged, it will be unstaged upon completion of the update. X */ int bcs_update_bl(bcs_obj_type bcs_obj, char *filename) { X bcs_file_info file_info; X int status = BCS_SUCCESS; X X file_info = bcs_get_file_info(bcs_obj, filename); X if (file_info == NULL) X { X bcs_message(bcs_obj, stderr, X "can't update baseline copy of %s since it " X "does not exist or cannot be read.", X filename); X return BCS_ERR_USER; X } X X if (fi_which(bcs_obj, file_info) != in_staging) X { X bcs_message(bcs_obj, stderr, X "cannot update baseline copy of %s since it " X "is not in the staging area.", filename); X return BCS_ERR_USER; X } X X if (! fi_has_baseline_counterpart(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stderr, X "cannot update baseline copy of %s since it " X "does not have a baseline counterpart.", X filename); X return BCS_ERR_USER; X } X X if (! cm_haslog(bcs_obj, X fi_clean_path(bcs_obj, X fi_bl_info(bcs_obj, file_info)))) X { X bcs_message(bcs_obj, stderr, X "cannot update baseline copy of %s since it " X "does not have a CM log.", filename); X return BCS_ERR_USER; X } X X if (fi_is_staged(bcs_obj, file_info) && fi_writable(bcs_obj, file_info)) X { X bcs_message(bcs_obj, stderr, X "not updating baseline copy of %s since the " X "staging area copy is writable. Is the file checked in?", X filename); X return BCS_ERR_USER; X } X X if (fi_writable(bcs_obj, fi_bl_info(bcs_obj, file_info))) X { X bcs_message(bcs_obj, stderr, X "not updating baseline copy of %s since the " X "baseline copy is writable.", filename); X return BCS_ERR_USER; X } X X if (cm_uptodate(bcs_obj, X fi_clean_path(bcs_obj, X fi_bl_info(bcs_obj, file_info)))) X { X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, "%s is already up-to-date.", X filename); X } X else X { X if (cm_locked(bcs_obj, X fi_clean_path(bcs_obj, X fi_bl_info(bcs_obj, file_info)))) X { X bcs_message(bcs_obj, stderr, X "WARNING: %s currently has outstanding locks." X " Please notify the owners of those locks that the " X "file is being updated.\n", filename); X } X X if (! cm_update_bl(bcs_obj, file_info)) X status = BCS_ERR_USER; X } X X if (status == BCS_SUCCESS) X { X if (fi_is_staged(bcs_obj, file_info)) X { X status = bcs_unstage(bcs_obj, filename, us_negative); X } X } X else X { X if (! bcs_obj->silent) X bcs_message(bcs_obj, stdout, X "update for %s failed; not unstaging file.", X filename); X } X X return status; } SHAR_EOF chmod 0444 bcs-2.0/lib/staging.c || echo 'restore of bcs-2.0/lib/staging.c failed' Wc_c="`wc -c < 'bcs-2.0/lib/staging.c'`" test 26143 -eq "$Wc_c" || echo 'bcs-2.0/lib/staging.c: original size 26143, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= bcs-2.0/lib/util.c ============== if test -f 'bcs-2.0/lib/util.c' -a X"$1" != X"-c"; then echo 'x - skipping bcs-2.0/lib/util.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting bcs-2.0/lib/util.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'bcs-2.0/lib/util.c' && /* X * $Id: util.c,v 1.23 1994/03/07 21:47:41 qjb Exp $ X * $Source: /local/tmp/bcs-2.0/lib/RCS/util.c,v $ X * $Author: qjb $ X * X * Copyright (C) 1994 E. Jay Berkenbilt X * X * This file is part of BCS. BCS may be distributed according to the terms X * of the General Public License and/or the Artistic License. See the X * `COPYING' and `Artistic' files with the source distribution for details. X * This notice must be kept intact when this file is distributed. X * X * This file contains various utility functions for use by BCS. X */ X #if !defined(lint) && !defined(CODECENTER) || defined(RCS_HDRS) /* Define a static function and call it. No warnings this way. */ static void rcsid(char *s) {rcsid("@(#)$Id: util.c,v 1.23 1994/03/07 21:47:41 qjb Exp $");} #endif /* !lint && !CODECENTER || RCS_HDRS */ X #include "bcs_p.h" #include X #define MSG_COLS 77 X static int copy_fd(bcs_obj_type bcs_obj, char *fromname, int from, X char *toname, int to) { X char buf[10 * BUFSIZ]; X int len; X X do { X if ((len = read(from, buf, sizeof(buf))) > 0) X { X if ((write(to, buf, len)) != len) X { X bcs_message(bcs_obj, stderr, "write to %s failed: %s", X toname, error_string(errno)); X return(BCS_ERR_SYSTEM); X } X } X else if (len == -1) X { X bcs_message(bcs_obj, stderr, "read from %s failed: %s", X fromname, error_string(errno)); X return(BCS_ERR_SYSTEM); X } X /* else len == 0; EOF */ X } while (len > 0); X X return BCS_SUCCESS; } X /* X * Compare two strings. For use by qsort. X */ int compare_strings(const void *a, const void *b) { X char *s1, *s2; X X s1 = *((char **) a); X s2 = *((char **) b); X X return (strcmp(s1, s2)); } X /* X * Print message breaking lines so that no line is longer than X * MSG_COLS characters. Print first line over all MSG_COLS columns. X * Print subsequent lines with three-character margin. X */ void bcs_message(bcs_obj_type bcs_obj, FILE *msgfile, const char *fmt, ...) { X va_list args; X char msg[BUFSIZ]; X int breakpoint; X char *p, *q; X X sprintf(msg, "%s: ", bcs_obj->whoami); X va_start(args, fmt); X vsprintf(msg + strlen(msg), fmt, args); X va_end(args); X X p = msg; X breakpoint = MSG_COLS - 3; X X if (strlen(msg) > MSG_COLS - breakpoint) X { X fwrite(p, MSG_COLS - breakpoint, 1, msgfile); X p += MSG_COLS - breakpoint; X } X X while (strlen(p) > breakpoint) X { X char t; X t = p[breakpoint]; X p[breakpoint] = '\0'; X q = strrchr(p, ' '); X p[breakpoint] = t; X if (q == NULL) X { X /* X * There are no spaces on this line. Don't break a word in X * the middle. X */ X if ((q = strchr(p, ' ')) == NULL) X q = p + strlen(p); X } X X fwrite(p, q - p, 1, msgfile); X fprintf(msgfile, "\n "); X p = q; X while (*p == ' ') X p++; X } X X if (p) X fprintf(msgfile, "%s\n", p); } X #ifdef DEBUG /* X * Print message (printf style) to stderr if flags passed in overlap X * with debug flags set X */ void bcs_dprint(bcs_obj_type bcs_obj, int flags, const char *fmt, ...) { X va_list args; X X if (bcs_obj->debug_flags & flags) X { X char msg[BUFSIZ]; X va_start(args, fmt); X sprintf(msg, "(debug): "); X vsprintf(msg + strlen(msg), fmt, args); X va_end(args); X bcs_message(bcs_obj, stderr, msg); X } } #endif /* DEBUG */ X /* X * This routine exists so that we don't have to check the return value X * of malloc. It will always either return allocated memory or exit. X */ void *bcsi_safe_malloc(char *whoami, int size) { X void *p; X if ((p = malloc(size)) == NULL) X { X fprintf(stderr, "%s: failure allocating %d bytes of memory; exiting.", X whoami, size); X exit(BCS_ERR_MEMORY); X } X X return p; } X X /* X * Safe version of strdup that calls safe malloc. We reimplemented X * strdup here since some operating systems don't have it anyway. X */ char *bcsi_safe_strdup(char *whoami, char *src) { X char *copy; X X copy = bcsi_safe_malloc(whoami, strlen(src) + 1); X strcpy(copy, src); X return copy; } X X int bcsi_safe_strcat(char *orig, char *cat, int len) { X if (strlen(orig) + strlen(cat) > len) X return FALSE; X else X { X strcat(orig, cat); X return TRUE; X } } X X /* X * Concatenate two paths and copy result into `result'. X */ void bcsi_concat_paths(char *result, char *first, char *second) { X if (result != first) X strcpy(result, first); X if (strlen(first) && strlen(second)) X strcat(result, PATH_SEP_STR); X strcat(result, second); } X X /* X * Given two path names, append the last component of the second path X * to the first path X */ void bcsi_append_last_component(char *orig, char *new) { X char *p; X X if ((strlen(orig)) && (strcmp(orig, PATH_SEP_STR) != 0)) X strcat(orig, PATH_SEP_STR); X if ((p = strrchr(new, PATH_SEP_CHAR))) X p++; X else X p = new; X strcat(orig, p); } X X /* X * If path is absolute, copy to dest. If it is relative, make X * dest absolute treating path as relative to file. This is X * useful for locating a file whose name is returned by readlink. X */ void bcsi_resolve_readlink(char *dest, char *linkpath, char *file) { X char *p; X X if (linkpath[0] == PATH_SEP_CHAR) X strcpy(dest, linkpath); X else X { X strcpy(dest, file); X if ((p = strrchr(dest, PATH_SEP_CHAR)) == NULL) X p = dest; X else X p++; X strcpy(p, linkpath); X } } X int bcsi_in_stringlist(char *str, bcs_stringlist list) { X int i; X X if (list) X { X for (i = 0; i < list->n; i++) X { X if (strcmp(str, list->data[i]) == 0) X return TRUE; X } X } X X return FALSE; } X bcs_stringlist bcsi_file_to_stringlist(bcs_obj_type bcs_obj, char *filename) { X bcs_stringlist list = NULL; X FILE *f; X X list = (bcs_stringlist) fl_new_flex_list(bcs_obj->whoami); X X if ((f = fopen(filename, "r")) != NULL) X { X char line[MAXPATHLEN]; X char *p; X X while ((fgets(line, sizeof(line) - 1, f)) != NULL) X { X if (line[strlen(line) - 1] == '\n') X line[strlen(line) - 1] = '\0'; X X /* strip leading and trailing blanks */ X for (p = line + strlen(line); (p > line) && ((! *p) || isspace(*p)); X p--); X *(p+1) = '\0'; X for (p = line; *p && isspace(*p); p++); X X fl_add_to_tail(bcs_obj->whoami, (flex_list) list, X bcsi_safe_strdup(bcs_obj->whoami, p)); X } X X fclose(f); X } X X return list; } X /* X * Ask a yes or no question. Return TRUE for a yes or FALSE for a no. X */ int bcsi_yn(char *question) { X char answer[BUFSIZ]; X X mem_zero(answer, sizeof(answer)); X while (TRUE) X { X printf("%s [y/n] ", question); X fflush(stdout); X if (fgets(answer, sizeof(answer) - 1, stdin) == NULL) X return FALSE; X if (answer[strlen(answer) - 1] == '\n') X answer[strlen(answer) - 1] = '\0'; X if ((strcasecmp(answer, "y") == 0) || X (strcasecmp(answer, "yes") == 0)) X return TRUE; X if ((strcasecmp(answer, "n") == 0) || X (strcasecmp(answer, "no") == 0)) X return FALSE; X printf("Please answer with `y' or `n'\n"); X } } X /* X * Return a sorted list of contents of a given directory X */ int bcsi_read_directory_entries(bcs_obj_type bcs_obj, char *name, X bcs_stringlist *entries) { X DIR *dir; X struct dirent *entry; X X if ((dir = opendir(name)) == NULL) X { X bcs_message(bcs_obj, stderr, "skipping directory %s; can't open: %s", X name, error_string(errno)); X return BCS_ERR_SYSTEM; X } X X *entries = (bcs_stringlist) fl_new_flex_list(bcs_obj->whoami); X X while ((entry = readdir(dir))) X { X while ((entry = readdir(dir)) != NULL) X { X if (! ((strcmp(entry->d_name, ".") == 0) || X (strcmp(entry->d_name, "..") == 0))) X { X fl_add_to_tail(bcs_obj->whoami, (flex_list) *entries, X bcsi_safe_strdup(bcs_obj->whoami, X entry->d_name)); X } X } X } X X closedir(dir); X X qsort((*entries)->data, (*entries)->n, sizeof(char *), compare_strings); X X return BCS_SUCCESS; } X /* X * Strip trailing PATH_SEP_CHAR's off of the end of path making sure X * not to strip it off if it's the only character. X */ void bcs_strip_dir_path(char *path) { X while ((strlen(path) > 1) && X (path[strlen(path) - 1] == PATH_SEP_CHAR)) X { X path[strlen(path) - 1] = '\0'; X } } X /* X * Split `str' by white-space and return a list of strings in `list'. X * `list' should already be initialized before calling this routine. X */ void bcsi_split_string(bcs_obj_type bcs_obj, bcs_stringlist list, char *str) { X char *p, *q; X char ch; X X if (list == NULL) X { X bcs_message(bcs_obj, stderr, X "bcsi_split_string called with null list"); X abort(); X } X X p = str; X X DPRINT((bcs_obj, BCS_DEBUG_LOGIC, "bcsi_split_string: %s", str)); X X while (*p) X { X /* skip spaces */ X for (; *p && isspace(*p); p++); X /* skip non-spaces */ X for (q = p; *q && (! isspace(*q)); q++); X ch = *q; X *q = '\0'; X DPRINT((bcs_obj, X BCS_DEBUG_LOGIC, "bcsi_split_string: adding %s", p)); X fl_add_to_tail(bcs_obj->whoami, (flex_list) list, X bcsi_safe_strdup(bcs_obj->whoami, p)); X *q = ch; X p = q; X } } X /* X * Make a directory along with all parent directories required. X */ int bcsi_make_directory(bcs_obj_type bcs_obj, char *dirname) { X char *p1; X char *p2; X char workpath[MAXPATHLEN]; X int status = BCS_SUCCESS; X X strcpy(workpath, dirname); X X /* Don't try to create "" */ X p1 = (workpath[0] == PATH_SEP_CHAR) ? workpath + 1 : workpath; X X /* X * Slide p2 along workpath to mark where we are so that X * we can make directories component by component. X * X * Don't try to be clever about links, etc. X */ X do X { X if ((p2 = strchr(p1, PATH_SEP_CHAR)) != NULL) X { X *p2 = '\0'; X } X if (!bcsi_exists(bcs_obj, workpath)) X { X if (mkdir(workpath, 0777)) { X bcs_message(bcs_obj, stderr, "can't make %s: %s", workpath, X error_string(errno)); X status = BCS_ERR_SYSTEM; X } X } X if (p2) X { X *p2 = PATH_SEP_CHAR; X p1 = p2 + 1; X } X } while (p2 && (! status)); X X return status; } X int bcsi_create_empty_file(bcs_obj_type bcs_obj, char *filename, int mode) { X int d; X X if ((d = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode)) == -1) X { X bcs_message(bcs_obj, stderr, "can't create %s: %s", filename, X error_string(errno)); X return BCS_ERR_SYSTEM; X } X else X { X if (close(d) == -1) X { X bcs_message(bcs_obj, stderr, "failed to close %s: %s", X filename, error_string(errno)); X return BCS_ERR_SYSTEM; X } X } X X return BCS_SUCCESS; } X /* X * Recursively remove this file. Then remove all references to it in the X * cache. We should proceed in this order so that the cache doesn't X * get repopulated during our lookups as we remove the file. X */ int bcsi_remove_file(bcs_obj_type bcs_obj, char *path) { X if (! bcs_obj->no_action) X { X path_cache path_info; X int status = BCS_SUCCESS; X enum { rm_file, rm_dir, rm_none } action = rm_none; X X path_info = bcsi_get_path_info(bcs_obj, path); X if (path_info == NULL) X { X /* file must not exist */ X return BCS_SUCCESS; X } X X if (pc_isdir(bcs_obj, path_info)) X { X char tmppath[MAXPATHLEN]; X char *p; X bcs_stringlist entries = NULL; X int i; X X if ((status = bcsi_read_directory_entries(bcs_obj, path, &entries))) X return status; X X strcpy(tmppath, path); X strcat(tmppath, PATH_SEP_STR); X p = tmppath + strlen(tmppath); X for (i = 0; i < entries->n; i++) X { X strcpy(p, entries->data[i]); X if ((status = bcsi_remove_file(bcs_obj, tmppath)) != X BCS_SUCCESS) X { X /* Don't return without deleting entries... */ X break; X } X } X X fl_delete_flex_list((flex_list *) &entries, free); X if (status) X return status; X else X action = rm_dir; X } X else X action = rm_file; X X switch (action) X { X case rm_file: X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "remove_file: unlink %s", X path)); X status = unlink(path); X break; X case rm_dir: X DPRINT((bcs_obj, BCS_DEBUG_VERBOSE, "remove_file: rmdir %s", X path)); X status = rmdir(path); X break; X default: X /* do nothing */ X break; X } X X /* X * Remove cache information regardless of whether the removal X * of the file succeeded in case the removal succeeded partially. X */ X pc_remove_path_info(bcs_obj, path_info); X X if (status == -1) X { X bcs_message(bcs_obj, stderr, "remove %s failed: %s", path, X error_string(errno)); X return BCS_ERR_SYSTEM; X } X } X X return BCS_SUCCESS; } X int bcsi_make_link(bcs_obj_type bcs_obj, char *from, char *to) { X if (! bcs_obj->no_action) X { X if (symlink(from, to) == -1) X { X bcs_message(bcs_obj, stderr, "link %s -> %s failed: %s", X from, to, error_string(errno)); X return BCS_ERR_SYSTEM; X } X } X X return BCS_SUCCESS; } X int bcsi_copy_file(bcs_obj_type bcs_obj, char *from, char *to) { X int in = -1; X int out = -1; X int status = BCS_SUCCESS; X struct stat statbuf; X X if (! bcs_obj->no_action) X { X if ((in = open(from, O_RDONLY, 0)) != -1) X { X if ((out = open(to, O_WRONLY | O_CREAT, 0666)) != -1) X { X status = copy_fd(bcs_obj, from, in, to, out); X if (fstat(in, &statbuf) != -1) X { X if (fchmod(out, statbuf.st_mode) == -1) X { X bcs_message(bcs_obj, stderr, "chmod %s failed: %s", X from, error_string(errno)); X status = BCS_ERR_SYSTEM; SHAR_EOF true || echo 'restore of bcs-2.0/lib/util.c failed' fi echo 'End of bcs-2.0 part 4' echo 'File bcs-2.0/lib/util.c is continued in part 5' echo 5 > _shar_seq_.tmp exit 0