// // aegis - project change supervisor // Copyright (C) 2001-2008 Peter Miller // // 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 3 of the License, 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, see // . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int show_removed_files = -1; int show_dot_files = -1; int recursive_flag; int long_flag = 0; int mode_flag = -1; static output::pointer mode_col; int attr_flag = -1; static output::pointer attr_col; int user_flag = -1; static output::pointer user_col; int group_flag = -1; static output::pointer group_col; int size_flag = -1; static output::pointer size_col; int when_flag = -1; static output::pointer when_col; static output::pointer name_col; static col::pointer col_ptr; static string_list_ty dirs; static change::pointer cp; static project_ty *pp; static time_t oldest; static time_t youngest; // // NAME // path_cat // // SYNOPSIS // string_ty *path_cat(string_ty *lhs, string_ty *rhs); // // DESCRIPTION // The path_cat function is used to join two strings together as // a file name path. If either is dot (".") the other is used. // Only if both are not the current directory ore they glued together // with a slash. // static string_ty * path_cat(string_ty *s1, string_ty *s2) { static string_ty *dot; if (!dot) dot = str_from_c("."); if (str_equal(s1, dot)) return str_copy(s2); if (str_equal(s2, dot)) return str_copy(s1); return os_path_join(s1, s2); } // // NAME // stat_stack // // SYNOPSIS // string_ty *stat_stack(string_ty *path, struct stat *st); // // DESCRIPTION // The stat_stack function is used to walk the directory search // stack, looking for the named file. On success, the stat structure // is filled in, and the resolved file name is returned. // // It is a fatal error if the file cannot be found. // static string_ty * stat_stack(string_ty *path, struct stat *st) { size_t j; string_ty *dir; string_ty *resolved_path; sub_context_ty *scp; int errno_old; for (j = 0;; ++j) { dir = stack_nth(j); if (!dir) break; resolved_path = path_cat(dir, path); #ifdef S_IFLNK if (!glue_lstat(resolved_path->str_text, st)) return resolved_path; errno_old = errno; if (errno_old != ENOENT) { scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", resolved_path); fatal_intl(scp, i18n("lstat $filename: $errno")); // NOTREACHED } #else if (!glue_stat(resolved_path->str_text, st)) return resolved_path; errno_old = errno; if (errno_old != ENOENT) { scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", resolved_path); fatal_intl(scp, i18n("stat $filename: $errno")); // NOTREACHED } #endif str_free(resolved_path); } scp = sub_context_new(); sub_errno_setx(scp, ENOENT); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("stat $filename: $errno")); // NOTREACHED return 0; } // // NAME // readdir_stack // // SYNOPSIS // void readdir_stack(string_ty *path, string_list_ty *result); // // DESCRIPTION // The readdir_stack function is used to walk the directory search // stack reading the named directory at each level. The results are // "unioned" together to provide a single view. // static void readdir_stack(string_ty *path, string_list_ty *result) { size_t j; string_ty *s; string_ty *dir; result->clear(); for (j = 0;; ++j) { dir = stack_nth(j); if (!dir) break; s = path_cat(dir, path); if (read_whole_dir__wla(s->str_text, result)) { sub_context_ty *scp; int errno_old; errno_old = errno; if (errno_old == ENOENT) { str_free(s); continue; } scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("read $filename: $errno")); // NOTREACHED } str_free(s); } } static void print_mode_column(struct stat *st) { // // First the type indicator // if (S_ISDIR(st->st_mode)) mode_col->fputc('d'); else if (S_ISCHR(st->st_mode)) mode_col->fputc('c'); else if (S_ISBLK(st->st_mode)) mode_col->fputc('b'); #ifdef S_ISFIFO else if (S_ISFIFO(st->st_mode)) mode_col->fputc('p'); #endif #ifdef S_ISLNK else if (S_ISLNK(st->st_mode)) mode_col->fputc('l'); #endif #ifdef S_ISSOCK else if (S_ISSOCK(st->st_mode)) mode_col->fputc('s'); #endif else mode_col->fputc('-'); // // Now the user bits // if (st->st_mode & S_IRUSR) mode_col->fputc('r'); else mode_col->fputc('-'); if (st->st_mode & S_IWUSR) mode_col->fputc('w'); else mode_col->fputc('-'); if (st->st_mode & S_IXUSR) { if (st->st_mode & S_ISUID) mode_col->fputc('s'); else mode_col->fputc('x'); } else { if (st->st_mode & S_ISUID) mode_col->fputc('S'); else mode_col->fputc('-'); } // // Now the group bits // if (st->st_mode & S_IRGRP) mode_col->fputc('r'); else mode_col->fputc('-'); if (st->st_mode & S_IWGRP) mode_col->fputc('w'); else mode_col->fputc('-'); if (st->st_mode & S_IXGRP) { if (st->st_mode & S_ISGID) mode_col->fputc('s'); else mode_col->fputc('x'); } else { if (st->st_mode & S_ISGID) mode_col->fputc('S'); else mode_col->fputc('-'); } // // Now the other bits // if (st->st_mode & S_IROTH) mode_col->fputc('r'); else mode_col->fputc('-'); if (st->st_mode & S_IWOTH) mode_col->fputc('w'); else mode_col->fputc('-'); if (st->st_mode & S_IXOTH) { #ifdef S_ISVTX if (st->st_mode & S_ISVTX) mode_col->fputc('t'); else #endif mode_col->fputc('x'); } else { #ifdef S_ISVTX if (st->st_mode & S_ISVTX) mode_col->fputc('T'); else #endif mode_col->fputc('-'); } } // // NAME // list_file // // SYNOPSIS // void list_file(string_ty *long_name, string-ty *short_name, // struct stat *st); // // DESCRIPTION // The list_file function is used to print the information about // the file onto the columnised output. // static void list_file(string_ty *long_name, string_ty *short_name, struct stat *st, string_ty *resolved_name) { fstate_src_ty *c_src; fstate_src_ty *p_src; string_ty *link = 0; c_src = cp ? change_file_find(cp, long_name, view_path_first) : 0; if (c_src && c_src->about_to_be_created_by) c_src = 0; if (c_src) { switch (c_src->action) { case file_action_remove: if (show_removed_files <= 0) { return; } break; case file_action_create: case file_action_modify: case file_action_insulate: case file_action_transparent: #ifndef DEBUG default: #endif // should be file_action_remove assert(!c_src->deleted_by); if (c_src->deleted_by) { if (show_removed_files <= 0) { return; } } break; } } p_src = project_file_find(pp, long_name, view_path_simple); if (p_src) { assert(!p_src->about_to_be_created_by); // hidden by viewpath switch (p_src->action) { case file_action_remove: if (show_removed_files <= 0) { return; } break; case file_action_create: case file_action_modify: case file_action_insulate: case file_action_transparent: #ifndef DEBUG default: #endif // should be file_action_remove assert(!p_src->deleted_by); if (p_src->deleted_by) { if (show_removed_files <= 0) { return; } } break; } } if (mode_col) { if (S_ISLNK(st->st_mode)) { string_ty *s2; // // If a link points to a relative file of exactly // the same name, then it's a link to a baseline. // Pretend it isn't a line. // os_become_orig(); link = os_readlink(resolved_name); os_become_undo(); if (link->str_text[0] != '/') { string_ty *s3; os_become_orig(); s2 = os_dirname(resolved_name); os_become_undo(); s3 = os_path_join(s2, link); str_free(s2); str_free(link); link = s3; } s2 = stack_relative(link); if (s2) { if (str_equal(s2, long_name)) { // nuke the link-ness st->st_mode = (st->st_mode & ~S_IFMT) | S_IFREG; str_free(link); link = 0; } // FIXME: need to re-relative the link, too str_free(s2); } } print_mode_column(st); } if (attr_col) { if (c_src) attr_col->fputc('C'); else if (p_src) attr_col->fputc('P'); else attr_col->fputc('-'); if (c_src) { char action_indicator = '?'; switch (c_src->action) { case file_action_create: action_indicator = 'c'; break; case file_action_modify: action_indicator = 'm'; break; case file_action_remove: action_indicator = 'r'; break; case file_action_insulate: action_indicator = 'i'; break; case file_action_transparent: action_indicator = 't'; break; } attr_col->fputc(action_indicator); } else { attr_col->fputc('-'); } fstate_src_ty *src = c_src ? c_src : p_src; if (src) { char usage_indicator = '?'; switch (src->usage) { case file_usage_source: usage_indicator = 's'; break; case file_usage_config: usage_indicator = 'c'; break; case file_usage_build: usage_indicator = 'b'; break; case file_usage_test: usage_indicator = 't'; break; case file_usage_manual_test: // // should this be 'm' ? // all the others are lower case // usage_indicator = 'T'; break; } attr_col->fputc(usage_indicator); } else { attr_col->fputc('-'); } } if (user_col) { struct passwd *pw; pw = getpwuid_cached(st->st_uid); if (pw) user_col->fputs(pw->pw_name); else user_col->fprintf("%d", (int)st->st_uid); } if (group_col) { struct group *gr; gr = getgrgid_cached(st->st_uid); if (gr) group_col->fputs(gr->gr_name); else group_col->fprintf("%d", (int)st->st_gid); } if (size_col) { size_col->fprintf("%8ld", (long)st->st_size); } if (when_col) { struct tm *the_time = localtime(&st->st_mtime); char buffer[100]; if (st->st_mtime < oldest || st->st_mtime > youngest) strftime(buffer, sizeof(buffer), "%b %d %Y", the_time); else strftime(buffer, sizeof(buffer), "%b %d %H:%M", the_time); when_col->fputs(buffer); } // // output the name // name_col->fputs(short_name); if (link) { if (long_flag) { name_col->fputs(" -> "); name_col->fputs(link); } str_free(link); } col_ptr->eoln(); } // // NAME // list_dir // // SYNOPSIS // void list_dir(string_ty *dirname); // // DESCRIPTION // The list_dir function is used to list the contents of a directory // onto the columnised output. // static void list_dir(string_ty *dirname) { string_list_ty wl; size_t j; os_become_orig(); readdir_stack(dirname, &wl); os_become_undo(); wl.sort(); string_list_ty more_dirs; for (j = 0; j < wl.nstrings; ++j) { string_ty *s; struct stat st; string_ty *resolved_path; if (show_dot_files <= 0 && wl.string[j]->str_text[0] == '.') continue; s = path_cat(dirname, wl.string[j]); os_become_orig(); resolved_path = stat_stack(s, &st); os_become_undo(); if (recursive_flag && (st.st_mode & S_IFMT) == S_IFDIR) more_dirs.push_back(s); list_file(s, wl.string[j], &st, resolved_path); str_free(resolved_path); str_free(s); } dirs.push_front(more_dirs); } // // NAME // list // // SYNOPSIS // void list(string_list_ty *paths); // // DESCRIPTION // The list function is used to list the named files and the contents // of the named directories onto the columnised output. // void list(string_list_ty *paths, project_ty *a_pp, change::pointer a_cp) { size_t j; int need_eject; int column = 0; int width; time_t when; when = now(); oldest = when - 6L * 30 * 24 * 60 * 60; youngest = when + 6L * 30 * 24 * 60 * 60; pp = a_pp; cp = a_cp; if (long_flag > 0) { if (mode_flag < 0) mode_flag = 1; if (attr_flag < 0) attr_flag = 1; if (user_flag < 0) user_flag = 1; if (group_flag < 0) group_flag = 1; if (size_flag < 0) size_flag = 1; if (when_flag < 0) when_flag = 1; } col_ptr = col::open((string_ty *)0); if (mode_flag > 0) { width = 10; mode_col = col_ptr->create(column, column + width, "Mode"); column += width + 1; } if (attr_flag) { width = 4; attr_col = col_ptr->create(column, column + width, "Attr"); column += width + 1; } if (user_flag > 0) { width = 8; user_col = col_ptr->create(column, column + width, "User\n--------"); column += width + 1; } if (group_flag > 0) { width = 8; group_col = col_ptr->create(column, column + width, "Group\n--------"); column += width + 1; } if (size_flag > 0) { width = 8; size_col = col_ptr->create(column, column + width, "Size\n--------"); column += width + 1; } if (when_flag > 0) { width = 12; when_col = col_ptr->create(column, column + width, "When\n------------"); column += width + 1; } name_col = col_ptr->create(column, 0, "File Name\n-----------"); col_ptr->title("Annotated Listing", ""); need_eject = 0; for (j = 0; j < paths->nstrings; ++j) { string_ty *resolved_path; struct stat st; string_ty *path; path = paths->string[j]; os_become_orig(); resolved_path = stat_stack(path, &st); os_become_undo(); if ((st.st_mode & S_IFMT) == S_IFDIR) dirs.push_back(path); else { need_eject = 1; list_file(path, path, &st, resolved_path); } str_free(resolved_path); } while (dirs.nstrings) { string_ty *path; if (need_eject) col_ptr->eject(); path = str_copy(dirs.string[0]); dirs.remove(path); col_ptr->title("Annotated Directory Listing", path->str_text); list_dir(path); need_eject = 1; } mode_col.reset(); attr_col.reset(); user_col.reset(); group_col.reset(); size_col.reset(); when_col.reset(); name_col.reset(); col_ptr.reset(); }