sh
/* $Id$ */
							/* Copyright (c) 2004-2012 Pierre Pronchery <khorben@defora.org> */
							/* This file is part of DeforaOS Unix sh */
							/* 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, version 3 of the License.
							 *
							 * 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 <http://www.gnu.org/licenses/>. */
							#include <unistd.h>
							#include <stdlib.h>
							#include <stdio.h>
							#include <string.h>
							#include <signal.h>
							#include "parser.h"
							#include "job.h"
							#include "sh.h"
							#define min(a, b) (a) < (b) ? (a) : (b)
							extern char ** environ;
							/* Prefs */
							static int _prefs_parse(Prefs * prefs, int argc, char * argv[])
							{
								int o;
								memset(prefs, 0, sizeof(Prefs));
								while((o = getopt(argc, argv, "csi")) != -1)
									switch(o)
									{
										case 'c':
											*prefs -= *prefs & PREFS_s;
											*prefs |= PREFS_c;
											break;
										case 's':
											*prefs -= *prefs & PREFS_c;
											*prefs |= PREFS_s;
											break;
										case 'i':
											*prefs |= PREFS_i;
											break;
										default:
											return -1;
									}
								return 0;
							}
							/* sh */
							char ** export;
							static int _sh(Prefs * prefs, int argc, char * argv[])
							{
								int ret;
								FILE * fp;
								setenv("PATH", "/usr/bin:/bin", 0);
								export = sh_export();
								if(*prefs & PREFS_c)
									ret = parser(prefs, *argv, NULL, argc - 1, &argv[1]);
								/* *prefs |= PREFS_s; FIXME necessary? */
								else if(argc == 0)
								{
									if(isatty(0) && isatty(2))
										*prefs |= PREFS_i;
									ret = parser(prefs, NULL, stdin, 0, NULL);
								}
								else
								{
									if((fp = fopen(argv[0], "r")) == NULL)
										ret = sh_error(argv[0], 127);
									else
									{
										ret = parser(prefs, NULL, fp, argc - 1, &argv[1]);
										fclose(fp);
									}
								}
								free(export);
								return ret;
							}
							/* sh_error */
							int sh_error(char const * message, int ret)
							{
								fputs("sh: ", stderr);
								perror(message);
								return ret;
							}
							char ** sh_export(void)
							{
								size_t cnt;
								char ** export;
								char ** e;
								size_t i;
								for(cnt = 0, e = environ; *e != NULL; cnt++, e++);
								if((export = malloc((cnt + 1) * sizeof(char*))) == NULL)
								{
									sh_error("malloc", 0);
									return NULL;
								}
								for(i = 0; i < cnt; i++)
									export[i] = environ[i];
								export[i] = NULL;
								return export;
							}
							/* sh_handler */
							static void _handler_signal(int signum);
							static void _sh_handler(int signum)
							{
								switch(signum)
								{
									case SIGINT:
										_handler_signal(SIGINT);
										break;
									case SIGTSTP:
										/* FIXME is this enough? */
										_handler_signal(SIGSTOP);
										break;
								}
							}
							static void _handler_signal(int signum)
							{
								job_kill_status(signum, JS_RUNNING);
							}
							/* usage */
							static int _usage(void)
							{
								fputs("Usage: sh [i][command_file [argument...]]\n\
							       sh -c[i]command_string[command_name [argument]]\n\
							       sh -s[i][argument]\n", stderr);
								return 1;
							}
							/* main */
							int main(int argc, char * argv[])
							{
								Prefs prefs;
								if(_prefs_parse(&prefs, argc, argv) != 0)
									return _usage();
								if(prefs & PREFS_c && optind == argc)
									return _usage();
								if(signal(SIGINT, _sh_handler) == SIG_ERR)
									sh_error("signal", 0); /* ignore error */
								if(signal(SIGTSTP, _sh_handler) == SIG_ERR)
									sh_error("signal", 0); /* ignore error */
								return _sh(&prefs, argc - optind, &argv[optind]);
							}
							