sh

/* $Id$ */
/* Copyright (c) 2011 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "sh.h"
#include "job.h"
#include "builtin.h"
extern char ** environ;
/* getopt_reset */
static void _getopt_reset(int argc, char * argv[])
/* FIXME */
{
optind = 1;
optarg = argv[1];
}
/* builtin_bg */
int builtin_bg(int argc, char * argv[])
{
/* FIXME */
return 0;
}
/* builtin_cd */
static int _cd_usage(void);
static int _cd_home(void);
static int _cd_previous(void);
static int _cd_chdir(int * prefs, char const * path);
int builtin_cd(int argc, char * argv[])
{
int o;
int prefs = 0;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "LP")) != -1)
switch(o)
{
case 'L':
prefs = 0;
break;
case 'P':
prefs = 1;
break;
default:
return _cd_usage();
}
if(argc - optind > 1)
return _cd_usage();
if(argc - optind == 0)
return _cd_home();
if(strcmp("-", argv[optind]) == 0)
return _cd_previous();
return _cd_chdir(&prefs, argv[optind]);
}
static int _cd_usage(void)
{
fputs("Usage: cd [-L | -P] directory\n\
cd -\n\
-L resolve symbolic links after parent directories\n\
-P resolve symbolic links before parent directories\n", stderr);
return 1;
}
static int _cd_home(void)
{
char * home;
int prefs = 0;
if((home = getenv("HOME")) == NULL)
{
fputs("sh: cd: $HOME is not set\n", stderr);
return 125;
}
return _cd_chdir(&prefs, home);
}
static int _cd_previous(void)
{
char * oldpwd;
int prefs = 0;
int ret;
if((oldpwd = getenv("OLDPWD")) == NULL)
{
fputs("sh: cd: $OLDPWD is not set\n", stderr);
return 125;
}
if((ret = _cd_chdir(&prefs, oldpwd)) == 0)
fprintf(stderr, "%s%s", oldpwd, "\n");
return ret;
}
static int _cd_chdir(int * prefs, char const * path)
{
char * p;
char const * oldpwd;
/* FIXME use prefs */
if(chdir(path) != 0)
return sh_error(path, 125); /* FIXME */
if((oldpwd = getenv("PWD")) != NULL)
if(setenv("OLDPWD", oldpwd, 1) != 0)
sh_error("setenv OLDPWD", 0);
if((p = getcwd(NULL, 0)) != NULL)
path = p;
if(setenv("PWD", path, 1) != 0)
sh_error("setenv PWD", 0);
free(p);
return 0;
}
/* builtin_exec */
int builtin_exec(int argc, char * argv[])
{
if(argc == 0)
return 0;
execvp(argv[0], argv);
exit(127);
}
/* builtin_exit */
static int _exit_usage(void);
int builtin_exit(int argc, char * argv[])
{
int status = 0;
char * p;
if(argc > 2)
return _exit_usage();
if(argc == 2)
{
status = strtol(argv[1], &p, 10);
if(*(argv[1]) == '\0' || *p != '\0' || status < 0
|| status > 255)
return _exit_usage();
}
exit(status);
return 0;
}
static int _exit_usage(void)
{
fputs("Usage: exit [n]\n", stderr);
return 1;
}
/* builtin_export */
static int _export_usage(void);
static void _export_list(void);
static void _export_do(char * arg);
int builtin_export(int argc, char * argv[])
{
int prefs = 0;
int o;
int i;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "p")) != -1)
switch(o)
{
case 'p':
prefs = 1;
break;
default:
return _export_usage();
}
if(prefs == 1 && optind == argc)
_export_list();
else if(prefs == 1 || optind == argc)
return _export_usage();
else
for(i = optind; i < argc; i++)
_export_do(argv[i]);
return 0;
}
static int _export_usage(void)
{
fputs("Usage: export name[=value]...\n\
export -p\n\
-p list all variables\n", stderr);
return 1;
}
static void _export_list(void)
{
char ** e;
char * p;
int i;
if(export == NULL)
return;
for(e = export; (p = *e) != NULL; e++)
{
printf("%s", "export ");
for(i = 0; p[i] != '\0' && p[i] != '='; i++)
fputc(p[i], stdout);
if(p[i] != '=')
{
fputc('\n', stdout);
continue;
}
printf("%s", "=\"");
for(i++; p[i] != '\0'; i++)
{
if(p[i] == '$' || p[i] == '"')
fputc('\\', stdout);
fputc(p[i], stdout);
}
printf("%s", "\"\n");
}
}
static void _export_do(char * arg)
{
int i;
char * e = arg;
char ** p;
for(i = 0; e[i] != '\0' && e[i] != '='; i++);
if(i == '\0')
e = "";
else
{
e[i] = '\0';
e++;
}
if(setenv(arg, e, 1) != 0)
{
sh_error(arg, 0);
return;
}
if((arg = getenv(arg)) == NULL)
return;
if(export != NULL)
for(i = 0; export[i] != NULL; i++);
if((p = realloc(export, (i+2) * sizeof(char*))) == NULL)
{
sh_error("malloc", 0);
return;
}
export = p;
export[i] = arg;
export[i+1] = NULL;
}
/* builtin_fg */
int builtin_fg(int argc, char * argv[])
{
/* FIXME */
return 0;
}
/* builtin_jobs */
static int _jobs_usage(void);
int builtin_jobs(int argc, char * argv[])
{
int (*func)(int argc, char * argv[]) = job_status;
int o;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "lp")) != -1)
switch(o)
{
case 'l':
func = job_list;
break;
case 'p':
func = job_pgids;
break;
default:
return _jobs_usage();
}
return func(argc - optind, &argv[optind]);
}
static int _jobs_usage(void)
{
fputs("Usage: jobs [-l | -p][job_id...]\n\
-l provide information about listed jobs (default: all)\n\
-p display process group leaders ID about listed jobs (default: all)\n",
stderr);
return 1;
}
/* builtin_read */
static int _read_usage(void);
static int _read_do(int argc, char * argv[]);
int builtin_read(int argc, char * argv[])
{
int o;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "r")) != -1)
switch(o)
{
case 'r':
/* FIXME */
break;
default:
return _read_usage();
}
return _read_do(argc-optind, &argv[optind]);
}
static int _read_usage(void)
{
fputs("Usage: read [-r] var...\n\
-r do not escape backslashes\n", stderr);
return 1;
}
static int _read_do(int argc, char * argv[])
{
int c;
char ** arg = argv;
char * value = NULL;
int value_cnt = 0;
char * p;
int ret = 0;
if(arg == NULL)
return 0;
for(c = fgetc(stdin);; c = fgetc(stdin))
{
/* FIXME backslash escaping is optional */
if(c == '\\')
{
if((c = fgetc(stdin)) == '\n')
continue;
}
else if(c == EOF || c == '\n'
|| (isblank(c) && *(arg+1) != NULL))
{
value[value_cnt] = '\0';
if(setenv(*arg, value, 1) != 0)
ret+=sh_error("setenv", 1);
value_cnt = 0;
if(*(arg+1) != NULL)
arg++;
if(c == EOF || c == '\n')
break;
continue;
}
if((p = realloc(value, value_cnt+2)) == NULL)
{
free(value);
return sh_error("malloc", 2);
}
value = p;
value[value_cnt++] = c;
}
free(value);
return ret == 0 ? 0 : 2;
}
/* set */
static int _set_usage(void);
static int _set_do(int argc, char * argv[]);
static int _set_list(void);
static int _set_unset(void);
int builtin_set(int argc, char * argv[])
{
int o;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "o")) != -1)
switch(o)
{
case 'o':
break;
default:
return _set_usage();
}
if(argc == optind)
{
if(optind > 1 && strcmp(argv[optind-1], "--") == 0)
return _set_unset();
return _set_list();
}
return _set_do(argc, argv);
}
static int _set_usage(void)
{
/* FIXME */
fputs("Usage: set -- [argument...]\n\
set -o\n\
set +o\n", stderr);
return 1;
}
static int _set_do(int argc, char * argv[])
{
char * p;
int ret = 0;
for(; optind < argc; optind++)
{
for(p = argv[optind]; *p != '\0' && *p != '='; p++);
if(*p != '=')
continue;
*p = '\0';
if(setenv(argv[optind], p+1, 1) != 0)
ret+=sh_error("setenv", 1);
*p = '=';
}
return ret;
}
static int _set_list(void)
{
char ** e;
for(e = environ; *e != NULL; e++)
printf("%s\n", *e);
return 0;
}
static int _set_unset(void)
{
char * e;
char buf[256];
unsigned int pos;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
for(e = *environ; e != NULL; e = *environ)
{
for(pos = 0; pos < sizeof(buf)-1 && e[pos] != '\0'
&& e[pos] != '='; pos++);
if(e[pos] != '=')
continue;
strncpy(buf, e, pos);
buf[pos] = '\0';
unsetenv(buf);
}
return 0;
}
/* umask */
static int _umask_usage(void);
static int _umask_get(void);
static int _umask_set(char * mask);
int builtin_umask(int argc, char * argv[])
{
int o;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "S")) != -1)
switch(o)
{
case 'S':
/* FIXME */
break;
default:
return _umask_usage();
}
if(argc > optind+1)
return _umask_usage();
if(optind == argc)
return _umask_get();
return _umask_set(argv[optind]);
}
static int _umask_usage(void)
{
fputs("Usage: umask [-s][mask]\n\
-S provide symbolic output\n", stderr);
return 1;
}
static int _umask_get(void)
{
mode_t mask;
mask = umask(0);
printf("%04o%s", mask, "\n");
umask(mask);
return 0;
}
static int _umask_set(char * mask)
{
mode_t mode;
char * p;
mode = strtol(mask, &p, 8);
if(mask[0] == '\0' || *p != '\0')
return _umask_usage();
umask(mode);
return 0;
}
/* unset */
static int _unset_usage(void);
int builtin_unset(int argc, char * argv[])
/* FIXME */
{
int o;
_getopt_reset(argc, argv);
while((o = getopt(argc, argv, "fv")) != -1)
switch(o)
{
case 'f':
case 'v':
/* FIXME */
break;
default:
return _unset_usage();
}
if(optind == argc)
return _unset_usage();
for(; optind < argc; optind++)
unsetenv(argv[optind]);
return 0;
}
static int _unset_usage(void)
{
fputs("Usage: unset [-fv] name [...]\n\
-f \n\
-v \n", stderr);
return 1;
}