/* PUTLOG -- Format and write a message to the logfile. This is called by * the putlog builtin (clputlog() in builtin.c) and in some places in the * CL (e.g., exec.c). */ void putlog ( struct task *tp, /* pointer to task or NULL */ char *usermsg ) { register char *ip, *op, *otop; register int n; char msg[SZ_LOGBUF], job[5]; char *pkg, *tname, *today(); extern int bkgno; /* job number if bkg job */ if (!keeplog()) return; /* If background job, format job number, but only if background * logging is enabled. */ if (firstask->t_flags & T_BATCH) { if (log_background()) sprintf (job, "[%d] ", bkgno); else return; } else job[0] = EOS; /* If a valid task pointer is given, get the package and task name. * Otherwise, assume it's an internal (cl) logging message. */ if (tp) { pkg = tp->t_ltp->lt_pkp->pk_name; tname = tp->t_ltp->lt_lname; } else { pkg = "cl"; tname = ""; } /* Format the message. Only use time, no day and date. Break long * messages into several lines. */ sprintf (msg, "# %8.8s %s%s%s %s- ", (today() + 4), pkg, (tp ? "." : ""), tname, job); otop = &msg[SZ_LOGBUF]; for (op=msg, n=0; *op && op < otop; op++) n++; for (ip=usermsg; (*op++ = *ip++) && op < otop; n++) if (n + 2 >= MAXCOL) { *op++ = '\\'; *op++ = '\n'; n = 0; } *(op-1) = '\n'; *op = EOS; put_logfile (msg); }
/* YY_GETC -- Called by the modified yylex() "input" macro in the lexical * analysis stage of the parser to get the next character from the input * stream. When EOF is reached on the stream, add the "bye" command to * the logfile. */ int yy_getc (FILE *fp) { register char ch; while ((ch = *ip_cmdblk++) == EOS) if (get_command (fp) == EOF) { if (currentask->t_flags & T_INTERACTIVE) if (log_commands()) put_logfile ("bye\n"); return (EOF); } return ((int) ch); }
/* YY_STARTBLOCK -- Terminate the last command block and start a new one. * Save old command block in history (if interactive) and in logfile (if * interactive, logging is enabled, and logflag argument is true). Even * if logging is enabled, a command will not be logged which aborts or is * interrupted. */ void yy_startblock ( int logflag ) { register char *ip; if (cldebug) eprintf ("startblock (%d)\n", logflag); /* Log cmdblk only if it was filled by an interactive task. We must * make the test when the new block is initialized since the write is * delayed. */ if (cmdblk_save) { /* Do not record commands which consist only of whitespace. */ for (ip=cmdblk; isspace (*ip); ip++) ; if (*ip != EOS) { /* Use the raw_cmdblk, saved in get_command(). */ put_history (raw_cmdblk); if (logflag && log_commands()) put_logfile (raw_cmdblk); } } if (cldebug) eprintf ("startblock: ifseen=%d\n", ifseen); if (!ifseen) { ip_cmdblk = op_cmdblk = cmdblk; *ip_cmdblk = EOS; } cmdblk_line = 0; cmdblk_save = (currentask->t_flags & T_INTERACTIVE); /* Mode switching of the lexical analyzer is enabled by this call * if the CL parameter lexmodes is set. Called between blocks * entered interactively and also during error recovery. */ lexinit(); }
/* GET_COMMAND -- Get command line from the input stream. If not interactive, * all we do is read the line into the cmdblk buffer. If called when parsing * command input to an interactive task, we must output a prompt before * reading in the command line. The prompt changes depending on whether or * not the command is the first in a command block (whether or not we have * closure). After reading the command, we check if it is a history directive * and process it if so. Otherwise we must still process it to expand any * history macros. Ignore all blank or comment lines. These are * any line in which the first non-blank character is a newline or a * '#'. This will make some things a bit more efficient, but is * actually to allow the if/else parsing to work properly. * * N.B.: We must directly or indirectly set ip_cmdblk so that yy_getc takes * the next character from the right place. This is either done directly * or by a call to yy_startblock. */ int get_command ( FILE *fp ) { register char *ip, *op; char raw_cmd[SZ_LINE+1]; /* buffer for raw command line */ char new_cmd[SZ_CMDBLK+1]; /* temporary for processed cmd */ int execute=1, temp, status; if (!(currentask->t_flags & T_INTERACTIVE) || parse_state == PARSE_PARAMS) { /* Ensure that searches through string terminate. */ cmdblk[SZ_LINE] = '\0'; ip_cmdblk = cmdblk; while (YES) { currentask->t_scriptln++; /* noninteractive mode */ status = (fgets (cmdblk, SZ_LINE, fp) == NULL ? EOF : OK); if (status == EOF) { cmdblk[0] = '\0'; break; } /* Check if this is a blank line. */ for (ip = cmdblk; *ip == ' ' || *ip == '\t'; ip++) ; if (*ip == '\n' || *ip == '\0') continue; /* Check for the #{ ... #} lexmode toggle sequences. These * are matched only at the beginning of a line. #{ sets * command mode on the command input stream and #} clears it. */ if (*ip == '#') { if (ip == cmdblk) { if (*(ip+1) == '{') { lex_setcpumode (fp); lexinit(); } else if (*(ip+1) == '}') { lex_clrcpumode (fp); lexinit(); } } continue; } break; } if (cldebug || echocmds()) eprintf ("%s", status == EOF ? "bye\n" : cmdblk); return (status); } raw_cmd[SZ_LINE] = '\0'; while (YES) { /* Prompt the user for a new command if the input buffer is empty. * The CL prompt clears raw mode in case it is left in effect by a * program abort. */ input_: if (c_fstati (fileno(fp), F_UNREAD) == 0) { if (c_fstati ((XINT)STDIN, F_RAW) == YES) c_fseti ((XINT)STDIN, F_RAW, NO); if (cmdblk_line == 0) pprompt (curpack->pk_name); else pprompt (NOCLOSURE); } /* Read the next command line. */ if (fgets (raw_cmd, SZ_LINE, fp) == NULL) return (EOF); /* Check for the #{ ... #} lexmode toggle sequences. These * are matched only at the beginning of a line. #{ sets * command mode on the command input stream and #} clears it. */ if (*(ip=raw_cmd) == '#') { if (*(ip+1) == '{') { lex_setcpumode (fp); lexinit(); } else if (*(ip+1) == '}') { lex_clrcpumode (fp); lexinit(); } } /* Skip leading whitespace. */ for (ip=raw_cmd; *ip == ' ' || *ip == '\t'; ip++) ; /* For interactive comments, make sure we store them in the * history and the logfile. This is so that users can add * comments into the logfile interactively. */ if (*ip == '#') { put_history (raw_cmd); if (log_commands()) put_logfile (raw_cmd); } else if (*ip != '\n' && *ip != '\0') { cmdblk_line++; break; } } /* If history directive, transform the directive into an executable * command block using the history data. Echo the new command as * if the user had typed it, for verification. */ if (*raw_cmd == HISTCHAR) { /* Use screen style history editing only if the CL parameter * "ehinit" contains the boolean variable "verify" (or if the * cmd is "ehistory", below). */ if (eh_verify) execute = edit_history_directive (raw_cmd+1, new_cmd); else { execute = process_history_directive (raw_cmd, new_cmd); fputs (new_cmd, currentask->t_stdout); } } else if (expand_history_macros (raw_cmd, new_cmd)) { fputs (new_cmd, currentask->t_stdout); } else { static char ehist[] = "ehistory"; int n; for (n=0, ip=raw_cmd, op=ehist; (*ip == *op); ip++, op++) n++; if (n > 0 && isspace (*ip)) { while (isspace (*ip)) ip++; execute = edit_history_directive (ip, new_cmd); } } /* If user deletes entire line go back and get another command. */ for (ip=new_cmd; isspace (*ip); ip++) ; if (*ip == EOS) { cmdblk_line = 0; execute = 1; goto input_; } /* Now move the processed command into the cmdblk buffer. If there * is not enough storage remaining in the cmdblk buffer, we have to * break the actual (large) command block up, calling yy_startblock to * start a new block, but without changing the line number within the * block. We must not let the history mechanism limit the size of a * command block. */ op_cmdblk = ip_cmdblk - 1; /* back up to EOS */ if (strlen (new_cmd) > (cmdblk + SZ_CMDBLK - op_cmdblk)) { temp = cmdblk_line; yy_startblock (LOG); cmdblk_line = temp; } ip_cmdblk = op = op_cmdblk; for (ip=new_cmd; (*op++ = *ip++) != EOS; ) ; /* Save the "raw command" here for use in yy_startblock. This is * to handle the problem of procedure script parsing overwriting * the raw command in cmdblk. */ strcpy (raw_cmdblk, cmdblk); if (!execute) yy_startblock (NOLOG); fflush (currentask->t_stdout); return (OK); }