port of netbsd's tr
[minix.git] / commands / de / de.c
bloba8d93e379a370a60c65b006c9774a7220a89a1d8
1 /****************************************************************/
2 /* */
3 /* de.c */
4 /* */
5 /* Main loop of the "Disk editor". */
6 /* */
7 /****************************************************************/
8 /* origination 1989-Jan-15 Terrence W. Holm */
9 /****************************************************************/
12 #include <minix/config.h>
13 #include <sys/types.h>
14 #include <sys/dir.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <ctype.h>
18 #include <errno.h>
19 #undef ERROR /* arrgghh, errno.h has this pollution */
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <signal.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include <minix/const.h>
29 #include <minix/type.h>
30 #include "../../servers/mfs/const.h"
31 #include "../../servers/mfs/type.h"
32 #include "../../servers/mfs/inode.h"
34 #include "de.h"
36 static char copyright[] = "de (c) Terrence W. Holm 1989";
39 _PROTOTYPE(void Push , (de_state *s ));
40 _PROTOTYPE(int Get_Base , (int *base ));
41 _PROTOTYPE(int Get_Filename , (de_state *s ));
42 _PROTOTYPE(int Get_Count , (char *units , unsigned long *result ));
43 _PROTOTYPE(void Exec_Shell , (void));
44 _PROTOTYPE(void Sigint , (int));
48 /****************************************************************/
49 /* */
50 /* main() */
51 /* */
52 /* Initialize. Handle the "-r" recovery option if */
53 /* specified, else enter the main processing loop. */
54 /* */
55 /****************************************************************/
58 void main( argc, argv )
59 int argc;
60 char *argv[];
63 static de_state s; /* it is safer not to put it on the stack
64 * and some things probably now rely on zero
65 * initialization
66 */
67 char *command_name = argv[0];
68 int recover = 0;
71 s.device_mode = O_RDONLY;
74 /* Parse arguments */
76 if ( argc == 3 && strcmp( argv[1], "-r" ) == 0 )
78 recover = 1;
79 --argc;
80 ++argv;
82 else if ( argc == 3 && strcmp( argv[1], "-w" ) == 0 )
84 s.device_mode = O_RDWR;
85 --argc;
86 ++argv;
89 if ( argc != 2 || *argv[1] == '-' )
91 fprintf( stderr, "Usage: %s [-w] /dev/device\n", command_name );
92 fprintf( stderr, " %s -r lost_file_name\n", command_name );
93 exit( 1 );
97 /* Set the effective id to the real id. This eliminates */
98 /* any increase in privilege done by a set-uid bit on the */
99 /* executable file. We want to be "root" for recovering */
100 /* files, because we must be able to read the device. */
101 /* However, in normal usage, de(1) should not let just */
102 /* anyone look at a file system, thus we drop the privilege. */
103 /* */
104 /* NOTE: There is a security hole when using "-r" with a */
105 /* set-uid de(1). Do not use set-uid root if there is any */
106 /* way to externally access your Minix system. */
108 if ( ! recover )
110 setuid( getuid() );
111 setgid( getgid() );
115 /* Set terminal characteristics, and ^C interrupt handler */
117 Save_Term();
119 if ( signal( SIGINT, SIG_IGN ) != SIG_IGN )
121 signal( SIGINT, Sigint );
122 signal( SIGQUIT, Sigint );
125 Set_Term();
127 if ( ! Init_Termcap() )
128 Error( "Requires a termcap entry" );
132 /* Get the device file name. If recovering, also open an output file. */
134 if ( recover )
136 char *dir_name;
137 char *file_name;
138 struct stat device_stat;
139 struct stat tmp_stat;
141 /* Split the path name into a directory and a file name. */
143 if ( strlen(argv[1]) > MAX_STRING )
144 Error( "Path name too long" );
146 if ( ! Path_Dir_File( argv[1], &dir_name, &file_name ) )
147 Error( "Recover aborted" );
149 /* Find the device holding the directory. */
151 if ( (s.device_name = File_Device( dir_name )) == NULL )
152 Error( "Recover aborted" );
155 /* The output file will be in /tmp with the same file name. */
157 strcpy( s.file_name, TMP );
158 strcat( s.file_name, "/" );
159 strcat( s.file_name, file_name );
162 /* Make sure /tmp is not on the same device as the file we */
163 /* are trying to recover (we don't want to use up the free */
164 /* i-node and blocks before we get a chance to recover them). */
166 if ( stat( s.device_name, &device_stat ) == -1 )
167 Error( "Can not stat(2) device %s", s.device_name );
169 if ( stat( TMP, &tmp_stat ) == -1 )
170 Error( "Can not stat(2) directory %s", TMP );
172 if ( device_stat.st_rdev == tmp_stat.st_dev )
173 Error( "Will not recover files on the same device as %s", TMP );
175 if ( access( s.file_name, F_OK ) == 0 )
176 Error( "Will not overwrite file %s", s.file_name );
179 /* Open the output file. */
181 if ( (s.file_f = fopen( s.file_name, "w" )) == NULL )
182 Error( "Can not open file %s", s.file_name );
184 /* Don't let anyone else look at the recovered file */
186 chmod( s.file_name, 0700 );
188 /* If running as root then change the owner of the */
189 /* restored file. If not running as root then the */
190 /* chown(2) will fail. */
192 chown( s.file_name, getuid(), getgid() );
194 else
196 s.device_name = argv[1];
197 s.file_name[ 0 ] = '\0';
201 /* Open the device file. */
204 struct stat device_stat;
205 off_t size;
207 if ( stat( s.device_name, &device_stat ) == -1 )
208 Error( "Can not find file %s", s.device_name );
210 if ( (device_stat.st_mode & S_IFMT) != S_IFBLK &&
211 (device_stat.st_mode & S_IFMT) != S_IFREG )
212 Error( "Can only edit block special or regular files" );
215 if ( (s.device_d = open( s.device_name, s.device_mode )) == -1 )
216 Error( "Can not open %s", s.device_name );
218 if ( (size = lseek( s.device_d, 0L, SEEK_END )) == -1 )
219 Error( "Error seeking %s", s.device_name );
221 if ( size % K != 0 )
223 Warning( "Device size is not a multiple of 1024" );
224 Warning( "The (partial) last block will not be accessible" );
229 /* Initialize the rest of the state record */
231 s.mode = WORD;
232 s.output_base = 10;
233 s.search_string[ 0 ] = '\0';
236 int i;
238 for ( i = 0; i < MAX_PREV; ++i )
240 s.prev_addr[ i ] = 0L;
241 s.prev_mode[ i ] = WORD;
246 sync();
248 Read_Super_Block( &s );
250 Read_Bit_Maps( &s );
252 s.address = 0L;
256 /* Recover mode basically performs an 'x' and an 'X' */
258 if ( recover )
260 ino_t inode = Find_Deleted_Entry( &s, argv[1] );
261 off_t size;
263 if ( inode == 0 )
265 unlink( s.file_name );
266 Error( "Recover aborted" );
269 s.address = ( (long) s.first_data - s.inode_blocks ) * K
270 + (long) (inode - 1) * s.inode_size;
272 Read_Block( &s, s.buffer );
275 /* Have found the lost i-node, now extract the blocks. */
277 if ( (size = Recover_Blocks( &s )) == -1L )
279 unlink( s.file_name );
280 Error( "Recover aborted" );
283 Reset_Term();
285 printf( "Recovered %ld bytes, written to file %s\n", size, s.file_name );
287 exit( 0 );
291 /* Enter the main loop, first time redraw the screen */
293 int rc = REDRAW;
298 if ( rc == REDRAW )
300 Read_Block( &s, s.buffer );
301 Draw_Screen( &s );
302 s.last_addr = s.address;
303 Draw_Pointers( &s );
306 else if ( rc == REDRAW_POINTERS )
308 s.offset = (unsigned) (s.address & ~ K_MASK);
309 Draw_Pointers( &s );
312 else if ( rc == ERROR )
314 Erase_Prompt();
315 putchar( BELL );
317 } while ( (rc = Process( &s, Arrow_Esc(Get_Char()) )) != EOF );
321 /* If there is an open output file that was never written to */
322 /* then remove its directory entry. This occurs when no 'w' */
323 /* or 'W' command occurred between a 'c' command and exiting */
324 /* the program. */
326 if ( s.file_name[0] != '\0' && ! s.file_written )
327 unlink( s.file_name );
330 Reset_Term(); /* Restore terminal characteristics */
332 exit( 0 );
337 /****************************************************************/
338 /* */
339 /* Get_Base( base ) */
340 /* */
341 /* Get a new base value. */
342 /* Returns REDRAW or ERROR. */
343 /* */
344 /****************************************************************/
348 int Get_Base( base )
349 int *base;
351 switch ( Get_Char() )
353 case 'h' : *base = 16;
354 break;
356 case 'd' : *base = 10;
357 break;
359 case 'o' : *base = 8;
360 break;
362 case 'b' : *base = 2;
363 break;
365 default : return( ERROR );
368 return( REDRAW );
373 /****************************************************************/
374 /* */
375 /* Process( state, input_char ) */
376 /* */
377 /* Determine the function requested by the */
378 /* input character. Returns OK, REDRAW, */
379 /* REDRAW_POINTERS, ERROR or EOF. */
380 /* */
381 /****************************************************************/
384 int Process( s, c )
385 de_state *s;
386 int c;
389 switch ( c )
391 case 'b' : /* Back up one block */
392 case ESC_PGUP :
394 if ( s->address == 0 )
395 return( ERROR );
397 s->address = (s->address - K) & K_MASK;
399 return( REDRAW );
402 case 'B' : /* Back up to home */
403 case ESC_HOME :
405 if ( s->address == 0 )
406 return( OK );
408 Push( s );
410 s->address = 0L;
412 return( REDRAW );
415 case 'c' : /* Change file name */
418 int rc = Get_Filename( s );
420 return( rc == OK ? REDRAW : rc );
424 case 'd' : /* Down */
425 case ESC_DOWN :
428 s->last_addr = s->address;
430 switch ( s->mode )
432 case WORD : s->address += 2;
434 if ( (s->address & PAGE_MASK) == 0 )
435 return( REDRAW );
437 return( REDRAW_POINTERS );
439 case BLOCK : s->address += 64;
441 if ( (s->last_addr & K_MASK) !=
442 (s->address & K_MASK) )
443 return( REDRAW );
445 return( REDRAW_POINTERS );
447 case MAP : s->address += 256;
449 return( REDRAW );
451 default : Error( "Internal fault (mode)" );
456 case 'f' : /* Forward one block */
457 case ' ' :
458 case ESC_PGDN :
460 if ( s->block == s->device_size - 1 )
461 return( ERROR );
463 s->address = (s->address + K) & K_MASK;
465 return( REDRAW );
468 case 'F' : /* Forward to end */
469 case ESC_END :
472 off_t last_block = ( (long) s->device_size - 1 ) * K;
474 if ( s->address == last_block )
475 return( OK );
477 Push( s );
479 s->address = last_block;
481 return( REDRAW );
485 case 'g' : /* Goto block */
488 unsigned long block;
490 if ( Get_Count( "Block?", &block ) )
492 if ( block >= s->zones )
494 Warning( "Block number too large" );
495 return( REDRAW );
498 Push( s );
500 s->address = (off_t) block * K;
502 return( REDRAW );
504 else
505 return( ERROR );
509 case 'G' : /* Goto block indirect */
512 unsigned block = *( (word_t *) &s->buffer[ s->offset ] );
514 if ( s->mode != WORD )
516 Warning( "Must be in visual mode \"word\"" );
517 return( REDRAW );
520 if ( block >= s->zones )
522 Warning( "Block number too large" );
523 return( REDRAW );
526 Push( s );
528 s->mode = BLOCK;
529 s->address = (long) block * K;
531 return( REDRAW );
535 case 'h' : /* Help */
536 case '?' :
538 Draw_Help_Screen( s );
540 Wait_For_Key();
542 return( REDRAW );
545 case 'i' : /* Goto i-node */
548 unsigned long inode;
550 if ( Get_Count( "I-node?", &inode ) )
552 if ( inode < 1 || inode > s->inodes )
554 Warning( "Illegal i-node number" );
555 return( REDRAW );
558 Push( s );
560 s->mode = WORD;
561 s->address = (off_t) (s->first_data - s->inode_blocks) * K
562 + (off_t) (inode - 1) * s->inode_size;
564 return( REDRAW );
566 else
567 return( ERROR );
571 case 'I' : /* Filename to i-node */
574 ino_t inode;
575 char *filename;
577 Draw_Prompt( "File name?" );
579 filename = Get_Line();
581 if ( filename == NULL || filename[0] == '\0' )
582 return( ERROR );
584 inode = Find_Inode( s, filename );
586 if ( inode )
588 Push( s );
590 s->mode = WORD;
591 s->address = ( (long) s->first_data - s->inode_blocks ) * K
592 + (long) (inode - 1) * s->inode_size;
595 return( REDRAW );
599 case 'l' : /* Left */
600 case ESC_LEFT :
603 s->last_addr = s->address;
605 switch ( s->mode )
607 case WORD : s->address = s->address - 32;
609 return( REDRAW );
611 case BLOCK : s->address -= 1;
613 if ( (s->last_addr & K_MASK) !=
614 (s->address & K_MASK) )
615 return( REDRAW );
617 return( REDRAW_POINTERS );
619 case MAP : s->address -= 4;
621 if ( (s->last_addr & ~ MAP_MASK) !=
622 (s->address & ~ MAP_MASK) )
623 return( REDRAW );
625 return( REDRAW_POINTERS );
627 default : Error( "Internal fault (mode)" );
632 case 'm' : /* Invoke a Minix shell */
634 Reset_Term();
636 Exec_Shell();
638 Set_Term();
640 return( REDRAW );
643 case 'n' : /* Search for next */
646 off_t addr;
648 if ( s->search_string[0] == '\0' )
650 Warning( "No search string defined" );
651 return( REDRAW );
654 Draw_Prompt( "Searching..." );
656 if ( (addr = Search( s, s->search_string )) == -1L )
658 Warning( "Search string not found" );
660 Wait_For_Key();
662 return( REDRAW );
665 Push( s );
666 s->address = addr;
668 return( REDRAW );
672 case 'o' : /* Set output base */
674 Draw_Prompt( "Output base?" );
676 return( Get_Base( &s->output_base ) );
679 case 'p' : /* Previous address */
682 int i;
684 s->address = s->prev_addr[ 0 ];
685 s->mode = s->prev_mode[ 0 ];
687 for ( i = 0; i < MAX_PREV - 1; ++i )
689 s->prev_addr[ i ] = s->prev_addr[ i + 1 ];
690 s->prev_mode[ i ] = s->prev_mode[ i + 1 ];
693 return( REDRAW );
697 case 'q' : /* Quit */
698 case EOF :
699 case CTRL_D :
701 return( EOF );
704 case 'r' : /* Right */
705 case ESC_RIGHT :
708 s->last_addr = s->address;
710 switch ( s->mode )
712 case WORD : s->address += 32;
714 return( REDRAW );
716 case BLOCK : s->address += 1;
718 if ( (s->last_addr & K_MASK) !=
719 (s->address & K_MASK) )
720 return( REDRAW );
722 return( REDRAW_POINTERS );
724 case MAP : s->address += 4;
726 if ( (s->last_addr & ~ MAP_MASK) !=
727 (s->address & ~ MAP_MASK) )
728 return( REDRAW );
730 return( REDRAW_POINTERS );
732 default : Error( "Internal fault (mode)" );
736 case 's' : /* Store word */
739 unsigned long word;
741 if ( s->mode != WORD )
743 Warning( "Must be in visual mode \"word\"" );
744 return( REDRAW );
747 if ( s->device_mode == O_RDONLY )
749 Warning( "Use -w option to open device for writing" );
750 return( REDRAW );
753 if ( Get_Count( "Store word?", &word ) )
755 if ( word != (word_t) word )
757 Warning( "Word is more than 16 bits" );
758 return( REDRAW );
760 Write_Word( s, (word_t) word );
762 return( REDRAW );
764 else
765 return( ERROR );
769 case 'u' : /* Up */
770 case ESC_UP :
773 s->last_addr = s->address;
775 switch ( s->mode )
777 case WORD : s->address -= 2;
779 if ( (s->last_addr & PAGE_MASK) == 0 )
780 return( REDRAW );
782 return( REDRAW_POINTERS );
784 case BLOCK : s->address -= 64;
786 if ( (s->last_addr & K_MASK) !=
787 (s->address & K_MASK) )
788 return( REDRAW );
790 return( REDRAW_POINTERS );
792 case MAP : s->address -= 256;
794 return( REDRAW );
796 default : Error( "Internal fault (mode)" );
801 case 'v' : /* Visual mode */
803 Draw_Prompt( "Visual mode?" );
805 switch ( Get_Char() )
807 case 'w' : s->mode = WORD;
808 break;
810 case 'b' : s->mode = BLOCK;
811 break;
813 case 'm' : {
814 /* Assume user knows if map mode is possible
815 char *tty = ttyname( 0 );
817 if ( tty == NULL ||
818 strcmp( tty, "/dev/tty0" ) != 0 )
819 Warning( "Must be at console" );
820 else
822 s->mode = MAP;
824 break;
827 default : return( ERROR );
830 return( REDRAW );
833 case 'w' : /* Write ASCII block */
835 if ( s->file_name[0] == '\0' )
837 int rc = Get_Filename( s );
839 if ( rc != OK )
840 return( rc );
843 /* We have a successfully opened file */
845 /* Eliminate non-ASCII characters */
847 int i;
848 char buf[ K ];
849 char *from = s->buffer;
850 char *to = buf;
852 for ( i = 0; i < K; ++i, ++from )
854 *to = *from & 0x7f;
856 if ( *to != '\0' && *to != '\177' )
857 ++to;
860 if ( fwrite( buf, 1, (int)(to - buf), s->file_f ) != to - buf )
861 Warning( "Problem writing out buffer" );
863 s->file_written = 1;
865 return( REDRAW );
869 case 'W' : /* Write block exactly */
871 if ( s->file_name[0] == '\0' )
873 int rc = Get_Filename( s );
875 if ( rc != OK )
876 return( rc );
879 /* We have a successfully opened file */
881 if ( fwrite( s->buffer, 1, K, s->file_f ) != K )
882 Warning( "Problem writing out buffer" );
884 s->file_written = 1;
886 return( REDRAW );
889 case 'x' : /* eXtract lost entry */
892 ino_t inode;
893 char *filename;
895 Draw_Prompt( "Lost file name?" );
897 filename = Get_Line();
899 if ( filename == NULL || filename[0] == '\0' )
900 return( ERROR );
902 inode = Find_Deleted_Entry( s, filename );
904 if ( inode )
906 Push( s );
908 s->mode = WORD;
909 s->address = ( (long) s->first_data - s->inode_blocks ) * K
910 + (long) (inode - 1) * s->inode_size;
913 return( REDRAW );
917 case 'X' : /* eXtract lost blocks */
920 int rc;
922 if ( s->mode != WORD )
924 Warning( "Must be in visual mode \"word\"" );
925 return( REDRAW );
929 /* Force a new output file name. */
931 if ( (rc = Get_Filename( s )) != OK )
932 return( rc );
935 Draw_Strings( s );
937 Erase_Prompt();
938 Draw_Prompt( "Recovering..." );
940 if ( Recover_Blocks( s ) == -1L )
941 unlink( s->file_name );
943 /* Force closure of output file. */
945 fclose( s->file_f );
946 s->file_name[ 0 ] = '\0';
948 return( REDRAW );
952 case '/' : /* Search */
953 case ESC_PLUS :
956 off_t addr;
957 char *string;
959 Draw_Prompt( "Search string?" );
961 string = Get_Line();
963 if ( string == NULL )
964 return( ERROR );
966 if ( string[0] != '\0' )
968 strcpy( s->search_string, string );
969 Draw_Strings( s );
972 else if ( s->search_string[0] == '\0' )
974 Warning( "No search string defined" );
975 return( REDRAW );
978 Erase_Prompt();
979 Draw_Prompt( "Searching..." );
981 if ( (addr = Search( s, s->search_string )) == -1L )
983 Warning( "Search string not found" );
985 Wait_For_Key();
987 return( REDRAW );
990 Push( s );
992 s->mode = BLOCK;
993 s->address = addr;
995 return( REDRAW );
999 default:
1000 return( ERROR );
1009 /****************************************************************/
1010 /* */
1011 /* Push( state ) */
1012 /* */
1013 /* Push current address and mode, used by the */
1014 /* commands B, F, g, G, i, I, n, x and /. This */
1015 /* information is popped by the 'p' command. */
1016 /* */
1017 /****************************************************************/
1020 void Push( s )
1021 de_state *s;
1024 int i;
1026 for ( i = MAX_PREV - 1; i > 0; --i )
1028 s->prev_addr[ i ] = s->prev_addr[ i - 1 ];
1029 s->prev_mode[ i ] = s->prev_mode[ i - 1 ];
1032 s->prev_addr[ 0 ] = s->address;
1033 s->prev_mode[ 0 ] = s->mode;
1041 /****************************************************************/
1042 /* */
1043 /* Get_Filename( state ) */
1044 /* */
1045 /* Read and check a filename. */
1046 /* */
1047 /****************************************************************/
1050 int Get_Filename( s )
1051 de_state *s;
1054 char *filename;
1055 char *name;
1056 FILE *f;
1058 Draw_Prompt( "File name?" );
1060 filename = Get_Line();
1062 if ( filename == NULL || filename[0] == '\0' )
1063 return( ERROR );
1066 for ( name = filename; *name != '\0'; ++name )
1067 if ( ! isgraph( *name ) )
1069 Warning( "File name contains non-graphic characters" );
1070 return( REDRAW );
1074 if ( access( filename, F_OK ) == 0 )
1076 Warning( "Will not overwrite file %s", filename );
1077 return( REDRAW );
1080 if ( (f = fopen( filename, "w" )) == NULL )
1082 Warning( "Can not open file %s", filename );
1083 return( REDRAW );
1086 /* If there is already an open output file then */
1087 /* close it. If it was never written to then */
1088 /* remove its directory entry. */
1090 if ( s->file_name[0] != '\0' )
1092 if ( ! s->file_written )
1093 unlink( s->file_name );
1095 fclose( s->file_f );
1098 strcpy( s->file_name, filename );
1099 s->file_f = f;
1100 s->file_written = 0;
1102 return( OK );
1110 /****************************************************************/
1111 /* */
1112 /* Get_Count() */
1113 /* */
1114 /* Read and check a number. Returns non-zero */
1115 /* if successful. */
1116 /* */
1117 /****************************************************************/
1120 int Get_Count( units, result )
1121 char *units;
1122 unsigned long *result;
1125 char *number;
1127 Draw_Prompt( units );
1129 number = Get_Line();
1131 if ( number == NULL || number[0] == '\0' )
1132 return( 0 );
1134 errno = 0;
1135 *result = strtoul( number, (char **) NULL, 0 );
1136 return( errno == 0 );
1144 /****************************************************************/
1145 /* */
1146 /* In_Use( bit, map ) */
1147 /* */
1148 /* Is the bit set in the map? */
1149 /* */
1150 /****************************************************************/
1153 int In_Use( bit, map )
1154 bit_t bit;
1155 bitchunk_t *map;
1158 return( map[ (int) (bit / (CHAR_BIT * sizeof (bitchunk_t))) ] &
1159 (1 << ((unsigned) bit % (CHAR_BIT * sizeof (bitchunk_t)))) );
1167 /****************************************************************/
1168 /* */
1169 /* Find_Inode( state, filename ) */
1170 /* */
1171 /* Find the i-node for the given file name. */
1172 /* */
1173 /****************************************************************/
1176 ino_t Find_Inode( s, filename )
1177 de_state *s;
1178 char *filename;
1181 struct stat device_stat;
1182 struct stat file_stat;
1183 ino_t inode;
1186 if ( fstat( s->device_d, &device_stat ) == -1 )
1187 Error( "Can not fstat(2) file system device" );
1189 #ifdef S_IFLNK
1190 if ( lstat( filename, &file_stat ) == -1 )
1191 #else
1192 if ( stat( filename, &file_stat ) == -1 )
1193 #endif
1195 Warning( "Can not find file %s", filename );
1196 return( 0 );
1199 if ( device_stat.st_rdev != file_stat.st_dev )
1201 Warning( "File is not on device %s", s->device_name );
1202 return( 0 );
1206 inode = file_stat.st_ino;
1208 if ( inode < 1 || inode > s->inodes )
1210 Warning( "Illegal i-node number" );
1211 return( 0 );
1214 return( inode );
1222 /****************************************************************/
1223 /* */
1224 /* Exec_Shell() */
1225 /* */
1226 /* Fork off a sub-process to exec() the shell. */
1227 /* */
1228 /****************************************************************/
1231 void Exec_Shell()
1234 int pid = fork();
1236 if ( pid == -1 )
1237 return;
1240 if ( pid == 0 )
1242 /* The child process */
1244 extern char **environ;
1245 char *shell = getenv( "SHELL" );
1247 if ( shell == NULL )
1248 shell = "/bin/sh";
1250 execle( shell, shell, (char *) 0, environ );
1252 perror( shell );
1253 exit( 127 );
1257 /* The parent process: ignore signals, wait for sub-process */
1259 signal( SIGINT, SIG_IGN );
1260 signal( SIGQUIT, SIG_IGN );
1263 int status;
1264 int w;
1266 while ( (w=wait(&status)) != pid && w != -1 );
1269 signal( SIGINT, Sigint );
1270 signal( SIGQUIT, Sigint );
1272 return;
1280 /****************************************************************/
1281 /* */
1282 /* Sigint() */
1283 /* */
1284 /* Terminate the program on an interrupt (^C) */
1285 /* or quit (^\) signal. */
1286 /* */
1287 /****************************************************************/
1290 void Sigint(n)
1291 int n;
1293 Reset_Term(); /* Restore terminal characteristics */
1295 putchar( '\n' );
1297 exit( 1 );
1305 /****************************************************************/
1306 /* */
1307 /* Error( text, ... ) */
1308 /* */
1309 /* Print an error message on stderr. */
1310 /* */
1311 /****************************************************************/
1314 #if __STDC__
1315 void Error( const char *text, ... )
1316 #else
1317 void Error( text )
1318 char *text;
1319 #endif
1322 va_list argp;
1324 Reset_Term();
1326 fprintf( stderr, "\nde: " );
1327 va_start( argp, text );
1328 vfprintf( stderr, text, argp );
1329 va_end( argp );
1330 if ( errno != 0 )
1331 fprintf( stderr, ": %s", strerror( errno ) );
1332 fprintf( stderr, "\n" );
1334 exit( 1 );