/* Copyright 1996 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* armsys.c: Copyright (C) Codemist Ltd., 1988 */ /* Copyright (C) Acorn Computers Ltd., 1988 */ /* Very ARM (and Brazil & Arthur) specific routines. */ /* NB to Lee, Alan etc - time() makes delicate tests to see what sort of */ /* machine it is being used with & may need adjustment as time goes by... */ /* version 11 */ #define __system_io 1 /* to get at flag bits */ #include "hostsys.h" /* things like _initio() */ #include "territory.h" #include "kernel.h" #include "swis.h" #include <stdio.h> /* for EOF */ #include <stdlib.h> /* for exit() */ #include <ctype.h> /* isprint(), isspace() */ #include <string.h> /* for strlen() */ #include <time.h> /* for clock */ #include <errno.h> /* IMPORTS */ extern int main(int argc, char **argv); /* the point of it all */ extern FILEHANDLE __dup(int new, int old); extern void _ctype_init(void); extern int _fprintf_lf(FILE *fp, const char *fmt, ...); extern int _sprintf_lf(char *buff, const char *fmt, ...); /* HIDDEN EXPORTS */ void _main(char *s, int (*main)(int, char **)); void _backtrace(int why, int *address, _kernel_unwindblock *uwb); const char *_clib_version(void); #define str(x) #x #define xstr(x) str(x) const char *_clib_version(void) { return LIB_SHARED "C Library vsn " xstr(__LIB_VERSION) "/" LIB_APCS " [" __DATE__ "]\n"; } #undef str #undef xstr static int unused[3]; /* timing things... */ /* struct bbctime objects are used to hold bbc/brazil 5-byte timer value - conventionally centi-seconds since 1-Jan-1900. */ struct bbctime {unsigned int l,h;}; #define _bbctime(bt) _kernel_osword(1, (int *)(bt)) static clock_t _time0; static clock_t _clock() { struct bbctime bt; _bbctime(&bt); return bt.l; } static void _clock_init() /* private - for initialisation */ { _time0 = _clock(); } static void _clock_ignore(clock_t t) /* ignores ticks from t to now - used to remove terminal input waits */ { _time0 += (_clock() - _time0) - t; } /* Exported... */ clock_t clock() { return _clock() - _time0; /* clock runs even if date not set */ } #define unbcd(a) (((a) & 0xf) + 10 * (((a)>>4) & 0xf)) time_t time(time_t *timer) /* this version gives the UNIX result of secs since 1-Jan-1970 */ { time_t result; int mctype; switch (mctype = _kernel_hostos()) { case 3: { /* This is true if we are on a Master series machine, in which */ /* case there is a built-in real time clock to be used. */ char bcdclock[28]; /* leave lots of room for _osword() to use */ int i; bcdclock[0] = 1; /* read in BCD format */ _kernel_osword(14, (int *)bcdclock); /* convert to natural binary bytes */ for (i = 0; i<7; i++) bcdclock[i] = unbcd(bcdclock[i]); i = bcdclock[0]; /* the year */ if (i < 70) result = -1; /* ANSI say (time)-1 if indeterminable */ else { int d = bcdclock[2] - 1, w = bcdclock[1], j; static int m[12] = { 31,29,31,30,31,30,31,31,30,31,30,31}; /* Months within the current year */ for (j = 1; j < w; j++) d += m[j-1]; /* Was this year a leap-year? */ if ((i & 3) != 0 && w >= 3) d -= 1; for (j = 70; j < i; j++) if ((j & 3) == 0) d += 366; else d += 365; /* Now adjust by the number of seconds into that day that we are */ result = bcdclock[6] + 60 * (bcdclock[5] + 60 * (bcdclock[4] + 24 * d)); } } break; #ifdef never /* I suppose one day the compiler should optimise these out */ case 7: /* Acorn Springboard */ case 1: /* Base level BBC micro - use 5-bit timer & assume set up for this */ case 6: /* Archimedes */ #endif default: /* a good a place as any for an unknown machine */ { struct bbctime bt, w, w2; if (mctype == 6) { unsigned v; _kernel_swi_regs r; bt.l = 3; /* presumably 'request time' arg */ _kernel_osword(14, (int *)&bt); /* read timer as 5 byte integer */ /* OS_Byte 0, 1 returns 6!!! so must look up Utility Module * version no. to find version of kernel. */ v = 0; /* Default to UTC if read timezone fails */ if (!_kernel_swi(Territory_ReadCurrentTimeZone, &r, &r)) v = r.r[1]; /* Offset from UTC to current TZ in cs */ bt.l += v; /* Unsigned addition */ if (bt.l < v) /* Carry == NOT Borrow */ bt.h++; if (v & (1u << 31)) bt.h--; /* Sign extend */ } else _bbctime(&bt); /* to two 3-byte things - for divide */ w.h = ((bt.h & 255) << 8) | (bt.l >> 24); w.l = bt.l & 0xffffff; /* turn csecs to secs */ w2.h = w.h / 100; w2.l = ((w.h % 100 << 24) | w.l) / 100; /* back to 8 byte binary */ bt.h = w2.h >> 8; bt.l = (w2.h << 24) | w2.l; /* normalise to Jan70 instead of Jan00... */ #define secs0070 (((unsigned)86400)*(365*70+17)) /* less than 2^32 */ if (bt.l < secs0070) bt.h--; bt.l -= secs0070; /* if high word is non-zero then date is unset/out of unix range... */ result = bt.h ? -1 : bt.l; } break; } if (timer) *timer = result; return result; } /* system dependent I/O routines ... */ /* Riscos has a second distinguished FILEHANDLE value, to indicate that */ /* a file is a keyboard and/or vdu, which can't be read or written using */ /* Riscos file operations (or at any rate, couldn't when the library was */ /* originally implemented). */ #define TTYHANDLE 0 #define istty(fh) ((fh) == TTYHANDLE) static int _error_recursion; void _sys_msg(const char *s) { /* write out s carefully for intimate system use. */ if ((stderr->__flag & _IOWRITE) && !_error_recursion) { _error_recursion = 1; fputc('\n', stderr); while (*s >= ' ') fputc(*s++, stderr); fputc('\n', stderr); _error_recursion = 0; } else { _ttywrite((unsigned char *)"\n", 1, 0); _ttywrite((unsigned char *)s, strlen(s), 0); _ttywrite((unsigned char *)"\n", 1, 0); } } #define LF '\n' #define CR '\r' static int isttyname(const char *s) { if (s[0] == ':' && (s[1]|0x20) == 't' && (s[2]|0x20) == 't' && s[3] == 0) return 1; /* string specification (:tt) of terminal stream */ return 0; } FILEHANDLE _sys_open(const char *filename, int openmode) { /* nasty magic number interface for openmode */ static const int modtab[6] = { /* r = */ 0x04c, /* r+ = */ 0x0cc, /* w = */ 0x4cc, /* w+ = */ 0x4cc, /* a = */ 0x3cc, /* a+ = */ 0x3cc }; if (isttyname(filename)) return TTYHANDLE; else { char *name = (char *)filename; /* yuk yuk yuk yuk yuk */ FILEHANDLE fh; int size = 16 * 1024; /* first try for created files */ int osmode = modtab[(openmode >> 1) & 7]; /* forget the 'b'inary bit */ _kernel_osfile_block fb; /* maybe stamp file with current datestamp */ if ((openmode & OPEN_T) || /* update timestamp requested */ (openmode & OPEN_W) || (openmode & ~OPEN_B) == OPEN_A) /* or mode = w, w+, or a */ { if (_kernel_osfile(9, name, &fb) == _kernel_ERROR) { if (_kernel_last_oserror()->errnum == 0x108c9) { (void)_kernel_osfile(9, name, &fb); /* to restore the error */ errno = -1; return NONHANDLE; /* (Protected disc) */ } } } retry_open: fh = _kernel_osfind(osmode & 0xff, name); if (osmode <= 0x0cc) { /* r or r+ */ if (fh == _kernel_ERROR) errno = -1; return (fh <= 0) ? NONHANDLE : /* not found */ fh; } else if (fh > 0) { if ((osmode == 0x4cc) || (size == 0)) if (_kernel_osargs(3, fh, 0) == _kernel_ERROR) { _kernel_osfind(0, (char *)fh); errno = -1; return NONHANDLE; } return fh; } else if (fh <= 0) { /* _kernel_osfile(11) creates an empty file of size 'size', of type */ /* given by fb.load, stamped with the current date & time */ fb.load = (openmode & 1) ? 0xffd : 0xfff; /* data : text */ fb.start = 0; for (; ; size >>= 1) { if (size < 512) { errno = -1; return NONHANDLE; } fb.end = size; if (_kernel_osfile(11, name, &fb) > 0) break; } size = 0; goto retry_open; } if (fh == _kernel_ERROR) errno = -1; return NONHANDLE; } } int _sys_istty(FILE *stream) { return istty(stream->__file); } int _sys_seek(FILEHANDLE fh, long pos) { if istty(fh) return 0; { int rc = _kernel_osargs(1, fh, (int)pos); if (rc == _kernel_ERROR) errno = -1; return rc; } } long _sys_flen(FILEHANDLE fh) { int rc = _kernel_osargs(2, fh, 0); if (rc == _kernel_ERROR) errno = -1; return rc; } int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode) { if (istty(fh)) return _ttywrite(buf, len, mode); else { _kernel_osgbpb_block b; b.dataptr = (void *)buf; b.nbytes = (int)len; if (_kernel_osgbpb(2, fh, &b) == _kernel_ERROR) { errno = -1; return _kernel_ERROR; } else return b.nbytes; } } int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode) { if (istty(fh)) return _ttyread(buf, (int)len, mode); else { _kernel_osgbpb_block b; b.dataptr = (void *)buf; b.nbytes = (int)len; if (_kernel_osgbpb(4, fh, &b) == _kernel_ERROR) { errno = -1; return _kernel_ERROR; } else return b.nbytes; } } int _sys_ensure(FILEHANDLE fh) { if (istty(fh)) return 0; { int rc = _kernel_osargs(0xFF, fh, 0); if (rc == _kernel_ERROR) errno = -1; return rc; } } int _sys_close(FILEHANDLE fh) { if (istty(fh)) return 0; { int rc = _kernel_osfind(0, (char *)fh); if (rc == _kernel_ERROR) errno = -1; return rc; } } int _ttywrite(const unsigned char *buf, unsigned int len, int flag) /* behaves like Kgbpb, but outputs to console. */ /* if 'flag' has _IOBIN set then LF's ('\n's) do not have CR suffixed. */ { while (len-- > 0) { int ch = *buf++; if (!(flag & _IOBIN)) { if (ch == '\n') { if (_kernel_oswrch(LF) < 0) return -1; ch = CR; } else if (ch < 32 && ch != 0x07 && !isspace(ch)) { if (_kernel_oswrch('|') < 0) return -1; ch = (ch & 31) | 64; } } if (_kernel_oswrch(ch) < 0) return -1; } return 0; /* number of chars unwritten */ } int _ttyread(unsigned char *buff, int size, int flag) { /* behaviour similar to Kgbpb but reads from keyboard, performing local */ /* edits as necessary. Control chars echo as ^<upper case>. */ /* AM: ignore clock ticks while waiting for keyboard */ /* If _IOBIN is set return 1st char read with no echo. * If _IONBF is set return 1st char read with echo. * Else read till CR LF ^D or EOF and return line. Refuse echo if buffer full. */ int count=0; time_t t = clock(); do { int ch; do { ch = _kernel_osrdch(); } while (ch == -27) /* ESCAPE */; if (flag & _IOBIN && ch != EOF && ch != _kernel_ERROR) buff[count++] = ch; /* binary - no echo */ else switch (ch) { case _kernel_ERROR: case EOF: /* see _osrdch for EOF */ case 0x04: _clock_ignore(t); /* ^D */ return(0x80000000+size-count); case '\n': /* treat CR as LF */ case '\r': if(count>=size) continue; _kernel_oswrch('\r'); _kernel_oswrch(LF); buff[count++] = '\n'; _clock_ignore(t); return(size-count); case 0x08: /* BS */ case 0x7f: if(count!=0) /* rubout */ { _kernel_oswrch(0x7f); if (buff[--count] < ' ') _kernel_oswrch(0x7f); } break; case 0x15: while(count>0) /* ctrl-U kills line */ { _kernel_oswrch(0x7f); if (buff[--count] < ' ') _kernel_oswrch(0x7f); } break; default: if(count>=size) continue; if (ch < ' ' && ch != 0x07) _kernel_oswrch('|'), _kernel_oswrch(ch | '@'); else _kernel_oswrch(ch); buff[count++] = ch; break; } } while (!(flag & _IOBIN+_IONBF)); _clock_ignore(t); return(size-count); } int remove(const char *pathname) { _kernel_osfile_block fb; if (_kernel_osfile(6, pathname, &fb) <= 0) return 1; return 0; } int rename(const char *old, const char *new) { char s[255]; if (strlen(old)+strlen(new) >= 255-8) return 1; /* tough */ strcpy(s, "rename "); strcpy(&s[7], old); strcat(&s[7], " "); strcat(&s[8], new); if (_kernel_oscli(s) < 0) return 1; return 0; } void _sys_tmpnam_(char *name, int sig) { if (_kernel_getenv("wimp$scrapdir", name, L_tmpnam-10) != NULL) strcpy(name, "$.tmp"); name += strlen(name); sprintf(name, ".x%.8x", sig); } static char *_getenv_value; char *getenv(const char *name) { if (_getenv_value == NULL) _getenv_value = malloc(256); if (_getenv_value == NULL || _kernel_getenv(name, _getenv_value, 256) != NULL) return NULL; else return _getenv_value; } void _terminate_getenv(void) { if (_getenv_value) free(_getenv_value); _getenv_value = NULL; } #ifdef DDE #define DDEUtils_SetCLSize 0x42581 #define DDEUtils_SetCL 0x42582 #endif int system(const char *string) { #define CALL 0 #define CHAIN 1 int rc; #ifdef DDE int type; char *cmd_string, *cmd; char *s, *t; _kernel_swi_regs r; #endif if (string==NULL) return (_kernel_processor_mode() == 0); if (_kernel_processor_mode() != 0) return -2; #ifdef DDE type = CALL; #endif if ((string[0] | 0x20)=='c') { char c = string[1] | 0x20; if (c=='a') { if ((string[2] | 0x20)=='l' && (string[3] | 0x20)=='l' && string[4]==':') string = string+5; } else if (c=='h') { if ((string[2] | 0x20)=='a' && (string[3] | 0x20)=='i' && (string[4] | 0x20)=='n' && string[5]==':') { string = string+6; #ifdef DDE type = CHAIN; #else _lib_shutdown(); _kernel_system(string, CHAIN); /* which never returns */ #endif } } } #ifdef DDE cmd_string = 0; if (strlen(string) > 255) { s = (char *)string; while (*s == ' ') s++; cmd = s; while (*s > ' ') s++; while (*s == ' ') s++; r.r[0] = strlen(s) + 1; if (!_kernel_swi(DDEUtils_SetCLSize, &r, &r)) { r.r[0] = (int)s; if (!_kernel_swi(DDEUtils_SetCL, &r, &r)) { cmd_string = malloc(s - cmd + 1); if (cmd_string) { s = cmd; t = cmd_string; while (*s > ' ') *t++ = *s++; *t++ = 0; string = cmd_string; } } } } if (type == CHAIN) _lib_shutdown(); rc = _kernel_system(string, type); if (cmd_string) free(cmd_string); #else rc = _kernel_system(string, CALL); #endif if (rc != 0) return rc; else { char *env; env = getenv("Sys$ReturnCode"); if (env == NULL) return 0; else return (atoi(env)); } /* -2 Kernel Fail, 0 = OK. Any other = result code from subprogram exit */ #undef CALL #undef CHAIN } char *decimal_point = "."; void _armsys_lib_init(void) { char *stdinfile = TTYFILENAME, *stdoutfile = TTYFILENAME, *stderrfile = TTYFILENAME; _getenv_value = NULL; _error_recursion = 0; _ctype_init(); /* C locale */ _exit_init(); /* must happen before exit() can be called */ _signal_init(); /* had better be done pretty early */ _clock_init(); /* set Cpu time zero point */ _init_alloc(); /* as had the allocator */ /* SIGINT events are not safe until about now. */ _raise_stacked_interrupts(); /* enable SIGINT */ if (!_kernel_client_is_module()) _initio(stdinfile, stdoutfile, stderrfile); } void _main(char *s, int (*main)(int, char **)) #define LINE_SIZE 256 #define BAD_REDIRECTION {goto bad_redirection;} #define NO_REDIRECTION goto no_redirection { char ch; static char **argv; static char *args; int curarg = 0, in_quotes = 0, was_quoted = 0; int after_file_name = 0, redirection = 0; int argc = 1, i = 0; int pre_digit = 0, dup_arg_1 = 0; char mode[2]; while (s[i] != 0) { while (isspace(s[i])) i++; while ((!isspace(s[i])) && s[i] != 0) i++; argc++; } argv = _sys_alloc(argc*sizeof(char *)); args = _sys_alloc(++i); _init_user_alloc(); i = 0; argc = 0; do { ch = *s++; if (!in_quotes) { if (ch == '"') { was_quoted = in_quotes = 1; ch = *s++; } else if ((i == curarg) && (!after_file_name)) { char *next = s - 1; pre_digit = -1; dup_arg_1 = -1; mode[0] = 0; mode[1] = 0; if (*next >= '0' && *next <= '9') pre_digit = *next++ - '0'; if ((*next == '>') || (*next == '<')) { if (*next == '>') /* stdout or stderr */ { mode[0] = 'w'; if (pre_digit == 0 || pre_digit > 2) BAD_REDIRECTION else if (*++next == '>') { mode[0] = 'a'; next++; } } else { char *p; next++; for (p = next; (*p != 0) && (*p != ' '); p++) if (*p == '>') NO_REDIRECTION; if (pre_digit > 0) BAD_REDIRECTION mode[0] = 'r'; } if (*next == '&') { if (pre_digit != -1) /* was a preceeding digit */ { if ((pre_digit > 0) && ((*++next >= '0') && (*next <= '2')) && (*next++ == (pre_digit % 2 + 1) + '0')) { /* 2>&1 or 1>&2 */ mode[0] = 0; /* no fopen required */ dup_arg_1 = pre_digit; } else BAD_REDIRECTION } else /* no preceeding digit */ { next++; dup_arg_1 = 2; pre_digit = (mode[0] != 'r'); /* default = 0 or 1 */ } } else if (pre_digit == -1) pre_digit = (mode[0] != 'r'); /* default = 0 or 1 */ if (mode[0] != 0) { after_file_name = 1; while (isspace(*next)) next++; if (*next == '"') { in_quotes = 1; next++; } } else if ((*next != 0) && (!isspace(*next))) BAD_REDIRECTION redirection = 1; s = next; ch = *s++; } } } if (in_quotes) { if ((ch == '\\') && ((*s == '"') || (*s == '\\'))) ch = *s++; else while (ch == '"') {in_quotes = !in_quotes; ch = *s++;} } no_redirection: if (ch != 0 && (in_quotes || !isspace(ch))) { args[i++] = ch; continue; } /* Assert: ((ch == 0) || (isspace(ch) && !in_quotes)) */ /* ------- possible end of arg ---------------------- */ if (i != curarg || was_quoted || (redirection && !after_file_name)) { /* end of arg */ args[i++] = 0; if (redirection) { if (after_file_name && freopen(&args[curarg], mode, &__iob[pre_digit]) == 0) { _fprintf_lf(stderr, _kernel_getmessage("can't open '%s' for I/O redirection", "C19"), &args[curarg]); fputc('\n', stderr); exit(EXIT_FAILURE); } if ((dup_arg_1 > -1) && (__dup(dup_arg_1, dup_arg_1 % 2 + 1) != TTYHANDLE)) { /* data to go to file */ FILE *s_new = &__iob[dup_arg_1]; FILE *s_old = &__iob[dup_arg_1 % 2 + 1]; setvbuf(s_new, _sys_alloc(LINE_SIZE), _IOLBF, LINE_SIZE); s_new->__flag |= _IOSBF; setvbuf(s_old, _sys_alloc(LINE_SIZE), _IOLBF, LINE_SIZE); s_old->__flag |= _IOSBF; } redirection = 0; after_file_name = 0; i = curarg; } else { argv[argc++] = &args[curarg]; curarg = i; } } if (ch != 0) { in_quotes = was_quoted = 0; } } while (ch != 0); if (in_quotes) { _fprintf_lf(stderr, _kernel_getmessage("missing double quotes", "C20")); fputc('\n', stderr); exit(EXIT_FAILURE); } argv[argc] = 0; /* for ANSI spec */ exit(/* hmm, relies on lots of things, but fast! */ (argc > 0 && (*(int *)argv[0] & ~0x20202020) == *(int *)"RUN") ? _call_client_2(main, argc-1, argv+1) : _call_client_2(main, argc, argv)); bad_redirection: _fprintf_lf(stderr, _kernel_getmessage("unsupported or illegal I/O redirection '%s'", "C21"), --s); fputc('\n', stderr); exit(EXIT_FAILURE); #undef NO_REDIRECTION #undef LINE_SIZE #undef BAD_REDIRECTION } static void p_in(_kernel_unwindblock *uwb) { _fprintf_lf(stderr, _kernel_getmessage("%x in ", "C22"), uwb->pc & ~0xfc000003); } #define ERROR_ILLEGALREAD 0x80800ea0 #define ERROR_ILLEGALWRITE 0x80800ea1 void _backtrace(int why, int *address, _kernel_unwindblock *uwb) { /* all the messages in the following should go to stderr */ FILE *err = stderr; char *lang = _kernel_language(uwb->pc); int cl_base, cl_limit; _kernel_swi_regs r; r.r[0] = 18; r.r[1] = (int)"SharedCLibrary"; cl_base = 0; cl_limit = 0; if (!_kernel_swi(OS_Module, &r, &r)) { cl_base = r.r[3]; cl_limit = cl_base + *((int *)(cl_base - 4)); } if (why==ERROR_ILLEGALREAD || why == ERROR_ILLEGALWRITE) { _fprintf_lf(err, _kernel_getmessage("(address %p)", "C23"), address); fputc('\n', err); } else { fputc('\n', err); _fprintf_lf(err, _kernel_getmessage("Postmortem requested", "C24")); } fputc('\n', err); /* Now unwind the stack. I keep track of sp here (as well as fp), but for */ /* the moment I make no use of it. */ while (uwb->fp!=0) { int *z, i, nargs, *argp; char *name = 0; int *fp = (int *) uwb->fp; _kernel_swi_regs r; if (lang[0]=='C' && lang[1]==0) { z = (int *)(fp[0] & 0x03fffffc); /* Note that when I save pc in a STM instruction it points 12 beyond the */ /* instruction, not just 8! Unless it's a StrongARM or similar. */ r.r[0] = 0; if (!_kernel_swi(OS_PlatformFeatures, &r, &r) && (r.r[0] & 8)) z -= 2; else z -= 3; /* If the word before the STM is itself STM sp!, {a1-a4} that shows */ /* where I should find args, and suggests that there are >= 5. */ /* (this needs to work whether sp is r12 or r13) */ argp = fp+1; if ((*(z-1) & ~0x00010000) ==0xe92c000f) { nargs = 5; } else { int mask = *z & 0xffff; /* Otherwise args were stored as part of the main STM. Find out where & */ /* how many. */ nargs = 0; while (mask != 0) { argp--; if (mask & 0xf) ++nargs; mask ^= mask & (-mask); } } /* Print args from the highest one downwards, in hex and decimal */ argp += nargs; while (nargs!=0) { int v = *(--argp); int carry; _fprintf_lf(err, _kernel_getmessage(" Arg%d: %#.8x %d", "C25"), nargs--, v, v); /* Indirect through addresses that might be legal... */ r.r[0] = (int)v; r.r[1] = (int)v + 4 * 4 - 1; if (!_kernel_swi_c(OS_ValidateAddress, &r, &r, &carry)) { if (!carry && !(v & 3)) { int *vp = (int *)v; fprintf(err, " -> [%#.8x %#.8x %#.8x %#.8x]", vp[0], vp[1], vp[2], vp[3]); } } fputc('\n', err); } /* I search up to 10 words before the STM looking for the marker that */ /* shows me where the function name is. */ for (i=0; i<10; i++) { int w = *--z; if ((w & 0xffff0000) == 0xff000000) { name = (char *)z - (w & 0xffff); break; } } p_in(uwb); if (name == 0) { if ((int)z >= cl_base && (int)z < cl_limit) _fprintf_lf(err, _kernel_getmessage("shared library function", "C26")); else _fprintf_lf(err, _kernel_getmessage("anonymous function", "C27")); } else _fprintf_lf(err, _kernel_getmessage("function %s", "C28"), name); } else { p_in(uwb); if (lang==NULL) { _fprintf_lf(err, _kernel_getmessage("unknown procedure", "C29")); } else { char *procname = _kernel_procname(uwb->pc); if (procname!=NULL) { _fprintf_lf(err, _kernel_getmessage("%s procedure %s", "C30"), lang, procname); } else { _fprintf_lf(err, _kernel_getmessage("anonymous %s procedure", "C31"), lang); } } } fputc('\n', err); if (_kernel_unwind(uwb, &lang) < 0) { fputc('\n', err); _fprintf_lf(err, _kernel_getmessage("stack overwritten", "C32")); fputc('\n', err); break; } } exit(EXIT_FAILURE); } /* end of armsys.c */