| 1 | /* elvprsv.c */ |
| 2 | |
| 3 | /* Author: |
| 4 | * Steve Kirkendall |
| 5 | * 14407 SW Teal Blvd. #C |
| 6 | * Beaverton, OR 97005 |
| 7 | * kirkenda@cs.pdx.edu |
| 8 | */ |
| 9 | |
| 10 | |
| 11 | /* This file contains the portable sources for the "elvprsv" program. |
| 12 | * "Elvprsv" is run by Elvis when Elvis is about to die. It is also |
| 13 | * run when the computer boots up. It is not intended to be run directly |
| 14 | * by the user, ever. |
| 15 | * |
| 16 | * Basically, this program does the following four things: |
| 17 | * - It extracts the text from the temporary file, and places the text in |
| 18 | * a file in the /usr/preserve directory. |
| 19 | * - It adds a line to the /usr/preserve/Index file, describing the file |
| 20 | * that it just preserved. |
| 21 | * - It removes the temporary file. |
| 22 | * - It sends mail to the owner of the file, saying that the file was |
| 23 | * preserved, and how it can be recovered. |
| 24 | * |
| 25 | * The /usr/preserve/Index file is a log file that contains one line for each |
| 26 | * file that has ever been preserved. Each line of this file describes one |
| 27 | * preserved file. The first word on the line is the name of the file that |
| 28 | * contains the preserved text. The second word is the full pathname of the |
| 29 | * file that was being edited; for anonymous buffers, this is the directory |
| 30 | * name plus "/foo". |
| 31 | * |
| 32 | * If elvprsv's first argument (after the command name) starts with a hyphen, |
| 33 | * then the characters after the hyphen are used as a description of when |
| 34 | * the editor went away. This is optional. |
| 35 | * |
| 36 | * The remaining arguments are all the names of temporary files that are |
| 37 | * to be preserved. For example, on a UNIX system, the /etc/rc file might |
| 38 | * invoke it this way: |
| 39 | * |
| 40 | * elvprsv "-the system went down" /tmp/elv_*.* |
| 41 | * |
| 42 | * This file contains only the portable parts of the preserve program. |
| 43 | * It must #include a system-specific file. The system-specific file is |
| 44 | * expected to define the following functions: |
| 45 | * |
| 46 | * char *ownername(char *filename) - returns name of person who owns file |
| 47 | * |
| 48 | * void mail(char *user, char *name, char *when) |
| 49 | * - tell user that file was preserved |
| 50 | */ |
| 51 | |
| 52 | #include <stdio.h> |
| 53 | #include "config.h" |
| 54 | #include "vi.h" |
| 55 | |
| 56 | /* We include ctype.c here (instead of including just ctype.h and linking |
| 57 | * with ctype.o) because on some systems ctype.o will have been compiled in |
| 58 | * "large model" and the elvprsv program is to be compiled in "small model" |
| 59 | * You can't mix models. By including ctype.c here, we can avoid linking |
| 60 | * with ctype.o. |
| 61 | */ |
| 62 | #include "ctype.c" |
| 63 | |
| 64 | void preserve P_((char *, char *)); |
| 65 | void main P_((int, char **)); |
| 66 | |
| 67 | #if AMIGA |
| 68 | BLK tmpblk; |
| 69 | # include "amiwild.c" |
| 70 | # include "amiprsv.c" |
| 71 | #endif |
| 72 | |
| 73 | #if OSK |
| 74 | # undef sprintf |
| 75 | #endif |
| 76 | |
| 77 | #if ANY_UNIX || OSK |
| 78 | # include "prsvunix.c" |
| 79 | #endif |
| 80 | |
| 81 | #if MSDOS || TOS |
| 82 | # include "prsvdos.c" |
| 83 | # define WILDCARD_NO_MAIN |
| 84 | # include "wildcard.c" |
| 85 | #endif |
| 86 | |
| 87 | |
| 88 | BLK buf; |
| 89 | BLK hdr; |
| 90 | BLK name; |
| 91 | int rewrite_now; /* boolean: should we send text directly to orig file? */ |
| 92 | |
| 93 | |
| 94 | |
| 95 | /* This function preserves a single file, and announces its success/failure |
| 96 | * via an e-mail message. |
| 97 | */ |
| 98 | void preserve(tname, when) |
| 99 | char *tname; /* name of a temp file to be preserved */ |
| 100 | char *when; /* description of when the editor died */ |
| 101 | { |
| 102 | int infd; /* fd used for reading from the temp file */ |
| 103 | FILE *outfp; /* fp used for writing to the recovery file */ |
| 104 | FILE *index; /* fp used for appending to index file */ |
| 105 | char outname[100]; /* the name of the recovery file */ |
| 106 | char *user; /* name of the owner of the temp file */ |
| 107 | #if AMIGA |
| 108 | char *prsvdir; |
| 109 | #endif |
| 110 | int i; |
| 111 | |
| 112 | /* open the temp file */ |
| 113 | infd = open(tname, O_RDONLY|O_BINARY); |
| 114 | if (infd < 0) |
| 115 | { |
| 116 | /* if we can't open the file, then we should assume that |
| 117 | * the filename contains wildcard characters that weren't |
| 118 | * expanded... and also assume that they weren't expanded |
| 119 | * because there are no files that need to be preserved. |
| 120 | * THEREFORE... we should silently ignore it. |
| 121 | * (Or loudly ignore it if the user was using -R) |
| 122 | */ |
| 123 | if (rewrite_now) |
| 124 | { |
| 125 | perror(tname); |
| 126 | } |
| 127 | return; |
| 128 | } |
| 129 | |
| 130 | /* read the header and name from the file */ |
| 131 | if (read(infd, hdr.c, BLKSIZE) != BLKSIZE |
| 132 | || read(infd, name.c, BLKSIZE) != BLKSIZE) |
| 133 | { |
| 134 | /* something wrong with the file - sorry */ |
| 135 | fprintf(stderr, "%s: truncated header blocks\n", tname); |
| 136 | close(infd); |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | /* If the filename block contains an empty string, then Elvis was |
| 141 | * only keeping the temp file around because it contained some text |
| 142 | * that was needed for a named cut buffer. The user doesn't care |
| 143 | * about that kind of temp file, so we should silently delete it. |
| 144 | */ |
| 145 | if (name.c[0] == '\0' && name.c[1] == '\177') |
| 146 | { |
| 147 | close(infd); |
| 148 | unlink(tname); |
| 149 | return; |
| 150 | } |
| 151 | |
| 152 | /* If there are no text blocks in the file, then we must've never |
| 153 | * really started editing. Discard the file. |
| 154 | */ |
| 155 | if (hdr.n[1] == 0) |
| 156 | { |
| 157 | close(infd); |
| 158 | unlink(tname); |
| 159 | return; |
| 160 | } |
| 161 | |
| 162 | if (rewrite_now) |
| 163 | { |
| 164 | /* we don't need to open the index file */ |
| 165 | index = (FILE *)0; |
| 166 | |
| 167 | /* make sure we can read every block! */ |
| 168 | for (i = 1; i < MAXBLKS && hdr.n[i]; i++) |
| 169 | { |
| 170 | lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); |
| 171 | if (read(infd, buf.c, BLKSIZE) != BLKSIZE |
| 172 | || buf.c[0] == '\0') |
| 173 | { |
| 174 | /* messed up header */ |
| 175 | fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); |
| 176 | close(infd); |
| 177 | return; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /* open the user's file for writing */ |
| 182 | outfp = fopen(name.c, "w"); |
| 183 | if (!outfp) |
| 184 | { |
| 185 | perror(name.c); |
| 186 | close(infd); |
| 187 | return; |
| 188 | } |
| 189 | } |
| 190 | else |
| 191 | { |
| 192 | /* open/create the index file */ |
| 193 | index = fopen(PRSVINDEX, "a"); |
| 194 | if (!index) |
| 195 | { |
| 196 | perror(PRSVINDEX); |
| 197 | exit(2); |
| 198 | } |
| 199 | |
| 200 | /* should be at the end of the file already, but MAKE SURE */ |
| 201 | fseek(index, 0L, 2); |
| 202 | |
| 203 | /* create the recovery file in the PRESVDIR directory */ |
| 204 | #if AMIGA |
| 205 | prsvdir = &PRSVDIR[strlen(PRSVDIR) - 1]; |
| 206 | if (*prsvdir == '/' || *prsvdir == ':') |
| 207 | { |
| 208 | sprintf(outname, "%sp%ld", PRSVDIR, ftell(index)); |
| 209 | } |
| 210 | else |
| 211 | #endif |
| 212 | sprintf(outname, "%s%cp%ld", PRSVDIR, SLASH, ftell(index)); |
| 213 | outfp = fopen(outname, "w"); |
| 214 | if (!outfp) |
| 215 | { |
| 216 | perror(outname); |
| 217 | close(infd); |
| 218 | fclose(index); |
| 219 | return; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | /* write the text of the file out to the recovery file */ |
| 224 | for (i = 1; i < MAXBLKS && hdr.n[i]; i++) |
| 225 | { |
| 226 | lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); |
| 227 | if (read(infd, buf.c, BLKSIZE) != BLKSIZE |
| 228 | || buf.c[0] == '\0') |
| 229 | { |
| 230 | /* messed up header */ |
| 231 | fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); |
| 232 | fclose(outfp); |
| 233 | close(infd); |
| 234 | if (index) |
| 235 | { |
| 236 | fclose(index); |
| 237 | } |
| 238 | unlink(outname); |
| 239 | return; |
| 240 | } |
| 241 | fputs(buf.c, outfp); |
| 242 | } |
| 243 | |
| 244 | /* add a line to the index file */ |
| 245 | if (index) |
| 246 | { |
| 247 | fprintf(index, "%s %s\n", outname, name.c); |
| 248 | } |
| 249 | |
| 250 | /* close everything */ |
| 251 | close(infd); |
| 252 | fclose(outfp); |
| 253 | if (index) |
| 254 | { |
| 255 | fclose(index); |
| 256 | } |
| 257 | |
| 258 | /* Are we doing this due to something more frightening than just |
| 259 | * a ":preserve" command? |
| 260 | */ |
| 261 | if (*when) |
| 262 | { |
| 263 | /* send a mail message */ |
| 264 | mail(ownername(tname), name.c, when); |
| 265 | |
| 266 | /* remove the temp file -- the editor has died already */ |
| 267 | unlink(tname); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | void main(argc, argv) |
| 272 | int argc; |
| 273 | char **argv; |
| 274 | { |
| 275 | int i; |
| 276 | char *when = "the editor went away"; |
| 277 | |
| 278 | #if MSDOS || TOS |
| 279 | /* expand any wildcards in the command line */ |
| 280 | _ct_init(""); |
| 281 | argv = wildexpand(&argc, argv); |
| 282 | #endif |
| 283 | |
| 284 | /* do we have a "-c", "-R", or "-when elvis died" argument? */ |
| 285 | i = 1; |
| 286 | if (argc >= i + 1 && !strcmp(argv[i], "-R")) |
| 287 | { |
| 288 | rewrite_now = 1; |
| 289 | when = ""; |
| 290 | i++; |
| 291 | #if ANY_UNIX |
| 292 | setuid(geteuid()); |
| 293 | #endif |
| 294 | } |
| 295 | #if OSK |
| 296 | else |
| 297 | { |
| 298 | setuid(0); |
| 299 | } |
| 300 | #endif |
| 301 | if (argc >= i + 1 && argv[i][0] == '-') |
| 302 | { |
| 303 | when = argv[i] + 1; |
| 304 | i++; |
| 305 | } |
| 306 | |
| 307 | /* preserve everything we're supposed to */ |
| 308 | while (i < argc) |
| 309 | { |
| 310 | preserve(argv[i], when); |
| 311 | i++; |
| 312 | } |
| 313 | } |