Pipe implementation on C++ - c++

I built a simple shell in order to execute pipe command like % ls | cat | number.When I execute normal pipe command, it works.But when I entering unknown command like% ls | las,I want to output error msg like Unknown command:[las],but the outputs of the program becomes weird.Here's my code about pipe:
void pipe_cmd(){
int index = 0;
char* cmds[command_num][100];
char* cmd[100];
while (index < command_num)
{
for(int i = 0; i < argc[index]; i++){
cmd[i] = const_cast<char*>(command[index][i].c_str());
cmds[index][i] = cmd[i];
}
cmds[index][argc[index]] = NULL;
index++;
}
int fd[2*(command_num - 1)];
for (int i = 0; i < command_num - 1; i++){
if(pipe(fd + 2 * i) == -1){
perror("Pipe failed");
exit(1);
}
}
pid_t pid;
for (int i = 0; i < command_num; i++)
{
pid = fork();
if(pid == 0){
if (i != 0){
dup2(fd[2 * (i - 1)], 0);
}
if(i != command_num - 1){
dup2(fd[2 * i + 1], 1);
}
for(int j = 0; j < 2*(command_num - 1);j++){
close(fd[j]);
}
if(execvp(cmds[i][0],cmds[i]) == -1){
cerr << "Unknown command: " << "[" << cmds[i][0] << "]." << endl;
}
} else{
if (i != 0){
close(fd[2 * (i - 1)]);
close(fd[2 * (i - 1) + 1]);
}
}
}
waitpid(pid, NULL, 0);
}
with my outputs look like:
% ls | las
Unknown command: [las].
% Unknown command: [README.md].
% Unknown command: [bin].
% % % Unknown command: [npshell.cpp].
% Unknown command: [test.html].

Before executing the command with execvp you have to check if all commands are at $PATH if it doesn't exist, which means the command entered by the user is not valid.
Here's an example for checking if a command exists or not, I have used vectors and string here as you are using c++.
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <vector>
std::vector<std::string> split_line(char *command) {
std::vector<std::string> commands;
char *token = strtok(command, " ");
while (token != NULL) {
commands.push_back(token);
token = strtok(NULL, " ");
}
return commands;
}
bool does_command_exists(std::vector<std::string> path, std::string command) {
char *c_command = const_cast<char *>(command.c_str());
std::vector<std::string> args = split_line(c_command);
// converting vector to char** (required for execvp)
char **argv = (char **)malloc(sizeof(char *) * args.size());
for (size_t i = 0; i < args.size(); i++) {
argv[i] = const_cast<char *>(args[i].c_str());
}
struct stat stats;
for (auto x : path) {
x = x.append("/");
x = x.append(argv[0]);
if (stat(x.c_str(), &stats) == 0) {
return true;
}
}
return false;
}
int main(void) {
std::vector<std::string> commands{"ls -l", "sort", "Something"};
char *env = getenv("PATH");
char *token = strtok(env, ":");
std::vector<std::string> path;
while (token != NULL) {
path.push_back(token);
token = strtok(NULL, ":");
}
for (auto command : commands) {
if (!does_command_exists(path, command)) {
std::cout << "Command: " << command << " does not exist "
<< "\n";
return -1;
}
}
// All commands are valid, now other stuff can be done
return 0;
}

Related

Enumerate removable drives on Linux and macOS

On Windows I am able to enumerate removable drives using FindFirstVolume, FindNextVolume, GetVolumePathNamesForVolumeName and the GetDriveType functions. How can I achieve this on Linux and macOS?
On macOS you need to work with IOKit: https://developer.apple.com/documentation/iokit
It can looks like the following code:
#include <cstdlib>
#include <iostream>
#include <mach/mach_port.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
std::string get_descriptor_idx(IOUSBDeviceInterface **dev, UInt8 idx);
int main(int argc, char *argv[]) {
/*
* Get the IO registry
*/
auto entry = IORegistryGetRootEntry(kIOMasterPortDefault);
if (entry == 0)
return EXIT_FAILURE;
io_iterator_t iter{};
auto kret = IORegistryEntryCreateIterator(entry, kIOUSBPlane, kIORegistryIterateRecursively, &iter);
if (kret != KERN_SUCCESS || iter == 0)
return EXIT_FAILURE;
io_service_t service {};
std::cout << std::endl;
while ((service = IOIteratorNext(iter))) {
IOCFPlugInInterface **plug = nullptr;
IOUSBDeviceInterface **dev = nullptr;
io_string_t path;
SInt32 score = 0;
IOReturn ioret;
kret = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);
IOObjectRelease(service);
if (kret != KERN_SUCCESS || plug == nullptr) {
continue;
}
/*
* USB
*/
ioret = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
static_cast<LPVOID *>((void *) &dev));
(*plug)->Release(plug);
if (ioret != kIOReturnSuccess || dev == nullptr) {
continue;
}
if (IORegistryEntryGetPath(service, kIOServicePlane, path) != KERN_SUCCESS) {
(*dev)->Release(dev);
continue;
}
std::cout << "USB Path: " << path << std::endl;
UInt8 si;
UInt16 u16v;
if ((*dev)->GetDeviceVendor(dev, &u16v) == kIOReturnSuccess)
std::cout << "VID: "<< u16v << std::endl;
if ((*dev)->GetDeviceProduct(dev, &u16v) == kIOReturnSuccess)
std::cout << "PID: "<< u16v << std::endl;
if ((*dev)->USBGetManufacturerStringIndex(dev, &si) == kIOReturnSuccess) {
std::cout << "Manufacturer: " << get_descriptor_idx(dev, si) << std::endl;
}
if ((*dev)->USBGetProductStringIndex(dev, &si) == kIOReturnSuccess) {
std::cout << "Product: " << get_descriptor_idx(dev, si) << std::endl;
}
(*dev)->Release(dev);
std::cout << std::endl;
}
IOObjectRelease(iter);
return 0;
}
/* a quick version */
std::string get_descriptor_idx(IOUSBDeviceInterface **dev, UInt8 idx)
{
IOUSBDevRequest request;
IOReturn ioret;
char buffer[4086] = { 0 };
CFStringRef cfstr;
CFIndex len;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBStringDesc << 8) | idx;
request.wIndex = 0x409;
request.wLength = sizeof(buffer);
request.pData = buffer;
ioret = (*dev)->DeviceRequest(dev, &request);
if (ioret != kIOReturnSuccess)
return "n/a";
if (request.wLenDone <= 2)
return "n/a";
cfstr = CFStringCreateWithBytes(nullptr, (const UInt8 *)buffer+2, request.wLenDone-2, kCFStringEncodingUTF16LE, 0);
len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8) + 1;
if (len < 0) {
CFRelease(cfstr);
return "n/a";
}
std::string str; str.resize(len);
CFStringGetCString(cfstr, str.data(), len, kCFStringEncodingUTF8);
CFRelease(cfstr);
return str;
}
In my case the result ıs:
USB Path: IOService:/AppleARMPE/arm-io/xxxx/USB2.1 Hub#01100000
VID: 1507
PID: 1552
Manufacturer: GenesysLogic
Product: USB2.1 USB

dup() creating file but not printing to it

I am trying to create a shell in c++. It creates a child process which executes a command and pipes the response back to the parent. I want to specify if the second argument of a command is -o then I would like to redirect the output of the command to a file. (output.txt).I used dup() to redirect output to my file. However, when I run the program and enter for example wc -o fileName the program creates the file output.txt but does not write to it when I specify to print the result of my child process.
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
#include <fcntl.h>
#include <vector>
#include <sys/wait.h>
int main(){
// array of file descriptors for parent and child
int filedes[2];
char foo[4096];
char** argv;
std::cout << "$$-> ";
char command[128];
std::cin.getline(command, 128);
if(strlen(command) != 0) {
std::vector<char *> args;
char *prog = strtok(command, " ");
char *tmp = prog;
while(tmp != NULL) {
args.push_back(tmp);
tmp = strtok(NULL, " ");
}
argv = new char *[args.size() + 1];
for (int k = 0; k < args.size(); k++) {
argv[k] = args[k];
}
argv[args.size()] = NULL;
}
char* newargc = argv[0];
char *newargv[] = {newargc,argv[2],NULL};
if(pipe(filedes) < 0){
std::cout << "There was an error creating the pipe";
}
int pid = fork();
if(pid == 0){
// writing to the pipe
// close read end of pipe
close(filedes[0]);
close(STDOUT_FILENO);
dup(filedes[1]);
if(strcmp(argv[1],(char*)"-o") == 0 ||strcmp(argv[1], (char*) "-b") == 0){
execvp(newargv[0], newargv);
}
else{
execvp(argv[0],argv);
}
}
else if (pid > 0) {
std::cout << "This is the parent process\n";
while(wait(NULL) > 0);
close(filedes[1]);
int output_fd = open("output.txt", O_CREAT, O_TRUNC, O_RDWR);
read(filedes[0], foo, sizeof(foo));
if(strcmp(argv[1],(char*)"-o") == 0){
close(STDOUT_FILENO);
dup(output_fd);
write(output_fd, foo, sizeof(foo));
}
else if(strcmp(argv[1], (char*) "-b") == 0){
int stdoutHolder = dup(STDOUT_FILENO);
close(STDOUT_FILENO);
dup(output_fd);
std::cout<< foo;
dup2(stdoutHolder, 1);
}
std::cout << foo;
}
//pid is less than 0 if error
else{
std::cout << "There is an error.";
}
return 0;
}

batch processing in a simple shell

Hey folks I am making a batch command function in a shell that reads from a txt file and pipes it to a child to exec.
I'm having an issue with exec. I suspect it is something with the null terminator. If I execl with an L and an explicit (char*)NULL the exec runs. If I execvp(argIn[0],argIn) nothing runs and returns a -1. If I execvp with an explicit (char*)NULL I get an error cannot convert char* to char* constant*. I read somewhere that it might be the g++ compiler giving me the error but the gcc compiler wouldn't give the error. Right now it won't compile with gcc though so I'm not sure if that's true. But it shouldn't need the explicit terminator anyway. I'm not sure if the '\0' I have stored is being passed to the exec right. It checks out when I pass it to other functions though so maybe that's not the solution.
Second, my for loop won't exec more than once which I think is more to do with the first solution. I can get the execl to fork with an index but I can't increment the index to point to the right token the next time through because the child should be wiping out my index right?
Anyway it's been 3 weeks of digging to figure out what's wrong. I failed the assignment. Probably going to fail the class. I don't know what else to try. So any help I would appreciate.
My question is why would the exec function not execute the program? I'm passing execvp(program name, program name, option, option, '\0') and not getting a result.
or
execl(program name, program name[index], option[index+1], option[index+1], (char*)NULL) and getting a result. They both seem to be following the parameters but only one is giving me a result.
#include<string.h>
#include<iostream>
#include<ctype.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
using namespace std;
int makearg(char s[], char**args[]);
int main(int argc, char *argv[])
{
char **argIn;
int argCount;
int pos = 0;
char str[500];
pid_t pid = fork();
for(int i = 0; i < 4; i++)
{
if(pid == 0)
{
while(fgets(str, 500, stdin) != NULL)
{
cout << "String loaded: " << str;
argCount = makearg(str, &argIn);
execvp(argIn[0],argIn); //nothing exec
// execl(argIn[0],argIn[0],argIn[1],argIn[2],(char*)NULL); //exec but requires index.
else if(pid < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
{
cout << "Parent waiting" << endl;
wait(NULL);
}
}
return 0;
}
int makearg(char s[], char**args[])
{
int counter = 1;
int tokenLen = 1;
int i = 0;
int j = 0;
int k = 0;
char arg1[50];
char * arg2;
strcpy(arg1, s);
//Count white space.
while (arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
counter++;
}
j++;
}
//Allocate the number of rows to be pointed to.
args[0] = (char**) malloc(counter + 1);
if(args[0] == NULL)
exit(1);
//Allocate the size of the c string arrays
j = 0;
while(arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
(*args)[i] = (char*)(malloc(tokenLen));
if((*args)[i] == NULL)
exit(1);
tokenLen = 0;
i++;
}
j++;
tokenLen++;
}
(*args)[i] = (char*)(malloc(tokenLen));
if ((*args)[i] == NULL)
exit(1);
//reset values
i = 0;
j = 0;
//Set arg2 to point to args row head. Transfer values from arg1 to arg2.
arg2 = ((*args)[i]);
while(arg1[j] != '\0')
{
if (arg1[j] != ' ' && arg1[j] != '\0' && arg1[j] != '\n')
{
arg2[k] = arg1[j];
k++;
}
else
{
arg2[k] = '\0';
i++;
k = 0;
arg2 = ((*args)[i]);
}
j++;
}
arg2[k] = '\0';
if (counter < 1)
{
return -1;
}
return counter;
}
I took your posted code, updated it to fix build errors and ran. I executed the simple command "ls" but I got the message
String loaded: ls
ls: cannot access '': No such file or directory
That indicated to me that makearg is not working correctly. Then, I added a function to help with diagnosing the problem.
void printArguments(char **args)
{
for ( int j = 0; args[j] != NULL; ++j )
{
printf("args[%d]: %s\n", j, args[j]);
}
}
and added a call to it from main, right after the call to makearg.
argCount = makearg(str, &argIn);
printArguments(argIn);
I got the output:
String loaded: ls
args[0]: ls
args[1]:
ls: cannot access '': No such file or directory
That indicated to me that makearg was not dealing with the end of the line correctly. It creates an empty argument.
I added couple of functions to trim whitespaces from the left and from the right. After that, the child process was able to execute "ls" correctly.
Here's the updated program.
#include<string.h>
#include<iostream>
#include<ctype.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
using namespace std;
int makearg(char s[], char**args[]);
void printArguments(char **args)
{
for ( int j = 0; args[j] != NULL; ++j )
{
printf("args[%d]: %s\n", j, args[j]);
}
}
int main(int argc, char *argv[])
{
char **argIn;
int argCount;
char str[500];
pid_t pid = fork();
for(int i = 0; i < 4; i++)
{
if(pid == 0)
{
while(fgets(str, 500, stdin) != NULL)
{
cout << "String loaded: " << str;
argCount = makearg(str, &argIn);
printArguments(argIn);
execvp(argIn[0],argIn); //nothing exec
}
}
else if(pid < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
{
cout << "Parent waiting" << endl;
wait(NULL);
}
}
return 0;
}
void trimWhiteSpacesLeft(char s[])
{
int i = 0;
for ( ; isspace(s[i]); ++i );
if ( i == 0 )
{
return;
}
int j = 0;
for (; s[i] != '\0'; ++j, ++i )
{
s[j] = s[i];
}
s[j] = '\0';
}
void trimWhiteSpacesRight(char s[])
{
int len = strlen(s);
int i = len-1;
for ( ; i >= 0; --i )
{
if ( !isspace(s[i]) )
{
break;
}
}
s[i+1] = '\0';
}
int makearg(char s[], char**args[])
{
int counter = 1;
int tokenLen = 1;
int i = 0;
int j = 0;
int k = 0;
char arg1[50];
char * arg2;
strcpy(arg1, s);
// Trim whitespaces from both ends.
trimWhiteSpacesLeft(arg1);
trimWhiteSpacesRight(arg1);
//Count white space.
while (arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' )
{
counter++;
}
j++;
}
//Allocate the number of rows to be pointed to.
args[0] = (char**) malloc(counter + 1);
if(args[0] == NULL)
exit(1);
//Allocate the size of the c string arrays
j = 0;
while(arg1[j] != '\0')
{
if (arg1[j] == ' ' || arg1[j] == '\0' || arg1[j] == '\n')
{
(*args)[i] = (char*)(malloc(tokenLen));
if((*args)[i] == NULL)
exit(1);
tokenLen = 0;
i++;
}
j++;
tokenLen++;
}
(*args)[i] = (char*)(malloc(tokenLen));
if ((*args)[i] == NULL)
exit(1);
//reset values
i = 0;
j = 0;
//Set arg2 to point to args row head. Transfer values from arg1 to arg2.
arg2 = ((*args)[i]);
while(arg1[j] != '\0')
{
if (arg1[j] != ' ' && arg1[j] != '\0' && arg1[j] != '\n')
{
arg2[k] = arg1[j];
k++;
}
else
{
arg2[k] = '\0';
i++;
k = 0;
arg2 = ((*args)[i]);
}
j++;
}
arg2[k] = '\0';
if (counter < 1)
{
return -1;
}
return counter;
}

Get device path based on USB VID:PID in Linux

If I plug in a device, say /dev/ttyUSB0 and I want to get the number 0 based on its VID:PID (found with lsusb), how could I do that in C++ Linux? I have this code to find one printer device, if it's helpful at all:
int printer_open (void)
{
char printer_location[] = "/dev/usb/lpX";
struct stat buf;
// continuously try all numbers until stat returns true for the connected printer
for (int i = 0; i < 10; i++)
{
printer_location[11] = '0' + i;
if (!stat (printer_location, &buf))
break;
}
return 0;
}
You could use libusb
apt-get install build-essential libudev-dev
Here is a good example: http://www.dreamincode.net/forums/topic/148707-introduction-to-using-libusb-10/
and here is the lib description: http://libusb.sourceforge.net/api-1.0/
int main() {
libusb_context *context = NULL;
libusb_device **list = NULL;
int rc = 0;
ssize_t count = 0;
rc = libusb_init(&context);
assert(rc == 0);
count = libusb_get_device_list(context, &list);
assert(count > 0);
for (size_t idx = 0; idx < count; ++idx) {
libusb_device *device = list[idx];
libusb_device_descriptor desc = {0};
rc = libusb_get_device_descriptor(device, &desc);
assert(rc == 0);
printf("Vendor:Device = %04x:%04x\n", desc.idVendor, desc.idProduct);
}
}
And if you compile your code don't forget to add the lib reference -I/usr/include/libusb-1.0/ and - lusb-1.0
libusb can't get it actually. So look at this file instead: /proc/bus/input/devices
Example line from the file:
I: Bus=0003 Vendor=1a2c Product=0c23 Version=0110
N: Name="USB USB Keyboard"
P: Phys=usb-0000:00:14.0-3/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/0003:1A2C:0C23.0015/input/input30
U: Uniq=
H: Handlers=sysrq kbd event10 leds
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff800000000007ff febeffdff3cfffff fffffffffffffffe
B: MSC=10
B: LED=7
This function gets the event number from the device with the matching VID:PID:
#include <string>
#include <iostream>
#include <fstream>
void open_device (std::string device_vid, std::string device_pid)
{
try
{
std::ifstream file_input;
std::size_t pos;
std::string device_path, current_line, search_str, event_str;
std::string device_list_file = "/proc/bus/input/devices";
bool vid_pid_found = false;
int fd = 0;
bool debug = true;
// 1. open device list file
file_input.open(device_list_file.c_str());
if (!file_input.is_open())
{
std::cerr << "file_input.open >> " << std::strerror(errno) << std::endl;
throw -2;
}
// 2. search for first VID:PID and get event number
search_str = "Vendor=" + device_vid + " Product=" + device_pid;
while (getline(file_input, current_line))
{
if (!vid_pid_found)
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
vid_pid_found = true;
search_str = "event";
}
}
else
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
event_str = current_line.substr(pos);
// find space and substring event##
pos = event_str.find(' ', 0);
event_str = event_str.substr(0, pos);
break;
}
}
}
// 3. build device path
device_path = "/dev/input/" + event_str;
if (debug) std::cout << "device_path = " << device_path << std::endl;
// 4. connect to device
fd = open (device_path.c_str(), O_RDONLY);
if (fd < 0)
{
std::cerr << "open >> errno = " << std::strerror(errno) << std::endl;
throw -3;
}
}
catch (const std::exception &e)
{
std::cerr << "e.what() = " << e.what() << std::endl;
throw -1;
}
return;
}

Getting a "vector subscript out of range" error when passing a vector into a function: c++ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to write a c++ program that assembles MIPS instructions. While debugging, it keeps throwing an error at line 74 of my main:
myassembler.add(lexed[i].labels[0], lexed[i].name, tokens, i);
my main is here:
#include <fstream>
#include <iostream>
#include <iomanip>
#include <memory>
#include <stdexcept>
#include <string>
#include <sstream>
#include <vector>
#include "exceptions.h"
#include "lexer.h"
#include "util.h"
#include "assembler.h"
std::string read_file(const std::string& name) {
std::ifstream file(name);
if (!file.is_open()) {
std::string error = "Could not open file: ";
error += name;
throw std::runtime_error(error);
}
std::stringstream stream;
stream << file.rdbuf();
return std::move(stream.str());
}
int main(int argc, char** argv) {
// Adjusting -- argv[0] is always filename.
--argc;
++argv;
if (argc == 0) {
std::cerr << "Need a file" << std::endl;
return 1;
}
assembler myassembler;
for (int i = 0; i < argc; ++i) {
std::string asmName(argv[i]);
if (!util::ends_with_subseq(asmName, std::string(".asm"))) {
std::cerr << "Need a valid file name (that ends in .asm)" << std::endl;
std::cerr << "(Bad name: " << asmName << ")" << std::endl;
return 1;
}
// 4 is len(".asm")
auto length = asmName.size() - string_length(".asm");
std::string baseName(asmName.begin(), asmName.begin() + length);
std::string objName = baseName + ".obj";
try {
auto text = read_file(asmName);
try {
auto lexed = lexer::analyze(text); // Parses the entire file and returns a vector of instructions
for (int i =0; i < (int)lexed.size(); i++){
if(lexed[i].labels.size() > 0) // Checking if there is a label in the current instruction
std::cout << "label = " << lexed[i].labels[0] << "\n"; // Prints the label
std::cout<< "instruction name = " << lexed[i].name<< "\n"; // Prints the name of instruction
std::cout << "tokens = ";
std::vector<lexer::token> tokens = lexed[i].args;
for(int j=0; j < (int)tokens.size(); j++){ // Prints all the tokens of this instruction like $t1, $t2, $t3
if (tokens[j].type == lexer::token::Integer)
std::cout << tokens[j].integer() << " ";
else
std::cout << tokens[j].string() << " ";
}
myassembler.add(lexed[i].labels[0], lexed[i].name, tokens, i);
myassembler.p();
std::cout << "\n\n\n";
}
} catch(const bad_asm& e) {
std::stringstream error;
error << "Cannot assemble the assembly code at line " << e.line;
throw std::runtime_error(error.str());
} catch(const bad_label& e) {
std::stringstream error;
error << "Undefined label " << e.what() << " at line " << e.line;
throw std::runtime_error(error.str());
}
} catch (const std::runtime_error& err) {
std::cout << err.what() << std::endl;
return 1;
}
}
/*getchar();*/
return 0;
}
assembler.h:
#include "lexer.h"
#include <fstream>
#include <vector>
#include <string>
struct symbol
{
std::string label = "";
int slinenum;
};
struct relocation
{
std::string instruct = "";
std::string label = "";
int rlinenum;
int rt = 0;
int rs = 0;
};
struct opcode
{
std::string instruct = "";
int opc = 0;
bool isloadstore = false;
int extType = 0;
bool isbranch = false;
};
struct function
{
std::string instruct = "";
int funct = 0;
bool isjr = false;
bool isshift = false;
};
struct regs
{
std::string name;
int num;
};
enum instrtype
{
R, I, neither
};
class assembler
{
public:
assembler();
void oinit(void);
void finit(void);
void rinit(void);
void printToFile(std::fstream &file);
void savesymb(std::string label, int line);
void saverel(std::string instr, std::string label, int line, int rt, int rs);
std::vector<int> formatr(std::string instr, lexer::token toke1, lexer::token toke2, lexer::token toke3, int line);
int formatr(std::string instr, lexer::token toke, int line);
std::vector<int> formati(std::string instr, lexer::token toke1, lexer::token toke2, lexer::token toke3, int line);
std::vector<int> formati(std::string instr, lexer::token toke1, lexer::token toke2, int line);
int findnum(std::string regname);
void add(std::string label, std::string instr, const std::vector<lexer::token> &tokens, int linen);
void secAdd(void);
int rassemble(std::string instr, int rd, int rs, int rt, int shamt);
int iassemble(std::string instr, int rt, int rs, int imm);
void p();
private:
std::vector<int> results;
std::vector<symbol> symbtable;
std::vector<relocation> reloctable;
std::vector<opcode> ops;
std::vector<function> functions;
std::vector<regs> registers;
instrtype type = neither;
};
and assembler.cpp:
// ECE 2500
// Project 1: myAssembler
// assembler.cpp
// Sheila Zhu
#include "lexer.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "assembler.h"
assembler::assembler()
{
oinit();
finit();
rinit();
}
void assembler::oinit()
{
opcode myop;
myop.instruct = "addi";
myop.opc = 8;
myop.extType = 1;
ops.push_back(myop);
// more of the same
}
void assembler::finit()
{
function myfunc;
myfunc.instruct = "add";
myfunc.funct = 32;
functions.push_back(myfunc);
// more of the same
}
void assembler::rinit()
{
regs myreg;
myreg.name = "$zero";
myreg.num = 0;
registers.push_back(myreg);
//more of the same
}
void assembler::printToFile(std::fstream &file)
{
for (int i = 0; i < (int)results.size(); i++)
file << results.at(i) << std::endl;
}
void assembler::savesymb(std::string label, int line)
{
symbol symb;
symb.label = label;
symb.slinenum = line * 4;
symbtable.push_back(symb);
}
void assembler::saverel(std::string instr, std::string label, int line, int rt, int rs)
{
relocation re;
re.instruct = instr;
re.label = label;
re.rlinenum = line;
re.rt = rt;
re.rs = rs;
}
int assembler::findnum(std::string regname)
{
for (int i = 0; i < (int)registers.size(); i++)
{
if (regname == registers.at(i).name)
return registers.at(i).num;
}
return -1;
}
std::vector<int> assembler::formatr(std::string instr, lexer::token toke1, lexer::token toke2, lexer::token toke3, int line)
{
int rd = 0, rs = 0, rt = 0, shamt = 0;
std::vector<int> x;
function currf;
for (int i = 0; i < (int)functions.size(); i++)
{
if (instr == functions.at(i).instruct)
currf = functions.at(i);
}
try
{
if (currf.isshift)
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rd = findnum(toke1.string());
if (rd == -1)
throw 2;
}
if (toke2.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
{
shamt = toke3.integer();
if (shamt < 0)
throw 3;
}
else
throw 1;
}
else
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rd = findnum(toke1.string());
if (rd == -1)
throw 2;
}
if (toke2.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke3.string());
if (rt == -1)
throw 2;
}
}
}
catch (int e)
{
if (e == 1)
std::cerr << "Wrong argument in line " << line << std::endl;
else if (e == 2)
std::cerr << "Invalid register name in line " << line << std::endl;
else
std::cerr << "Shift amount cannot be negative in line " << line << std::endl;
}
x.push_back(rd);
x.push_back(rs);
x.push_back(rt);
x.push_back(shamt);
return x;
}
int assembler::formatr(std::string instr, lexer::token toke, int line)
{
int rs = 0;
try
{
if (toke.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke.string());
if (rs == -1)
throw 2;
}
}
catch (int e)
{
if (e == 1)
std::cerr << "Wrong argument in line " << line << std::endl;
else
std::cerr << "Invalid register name in line " << line << std::endl;
}
return rs;
}
std::vector<int> assembler::formati(std::string instr, lexer::token toke1, lexer::token toke2, lexer::token toke3, int line)
{
int rt = 0, rs = 0, imm = 0;
std::vector<int> x;
opcode currop;
for (int i = 0; i < (int)ops.size(); i++)
{
if (instr == ops.at(i).instruct)
currop = ops.at(i);
}
try
{
if (currop.isbranch)
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke2.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
imm = toke3.integer();
else
saverel(instr, toke3.string(), line, rt, rs);
}
else if (currop.isloadstore)
{
if ((instr == "lbu") || (instr == "sb"))
{
if (toke2.type == lexer::token::String)
throw 1;
else
{
if (toke2.integer() < 0)
imm = (0xFFFF << 16) + (0xFF << 8) + toke2.integer();
else
imm = toke2.integer();
}
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
}
else
{
if (toke2.type == lexer::token::String)
throw 1;
else
{
if (toke2.integer() < 0)
imm = (0xFFFF << 16) + toke2.integer();
else
imm = toke2.integer();
}
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
}
}
else
{
if ((instr == "andi") || (instr == "ori"))
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke2.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
imm = toke3.integer();
else
throw 1;
}
else
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke2.type == lexer::token::Integer)
throw 1;
else
{
rs = findnum(toke2.string());
if (rs == -1)
throw 2;
}
if (toke3.type == lexer::token::Integer)
{
if (toke3.integer() < 0)
imm = (0xFFFF << 16) + toke2.integer();
else
imm = toke3.integer();
}
else
throw 1;
}
}
}
catch (int e)
{
if (e == 1)
std::cerr << "Wrong argument in line " << line << std::endl;
else
std::cerr << "Invalid register name in line " << line << std::endl;
}
x.push_back(rt);
x.push_back(rs);
x.push_back(imm);
return x;
}
std::vector<int> assembler::formati(std::string instr, lexer::token toke1, lexer::token toke2, int line)
{
int rt = 0, imm = 0;
std::vector<int> rval;
try
{
if (toke1.type == lexer::token::Integer)
throw 1;
else
{
rt = findnum(toke1.string());
if (rt == -1)
throw 2;
}
if (toke2.type == lexer::token::String)
throw 1;
else
imm = toke2.integer();
}
catch (int e)
{
if (e == 1)
std::cerr << "Wrong argument in line " << line << std::endl;
else
std::cerr << "Invalid register name in line " << line << std::endl;
}
rval.push_back(rt);
rval.push_back(imm);
return rval;
}
void assembler::add(std::string label, std::string instr, const std::vector<lexer::token> &token, int linen)
{
int assembled = 0, rd = 0, rt = 0;
std::vector<int> argh;
int arg;
if (label.length() > 0)
savesymb(label, linen);
for (int i = 0; i < (int)functions.size(); i++)
{
if (instr == functions.at(i).instruct)
type = R;
}
for (int i = 0; i < (int)ops.size(); i++)
{
if (instr == ops.at(i).instruct)
type = I;
}
if (type == R)
{
try
{
if (instr == "jr")
{
if ((int)token.size() == 1)
{
arg = formatr(instr, token.at(0), linen);
assembled = rassemble(instr, rd, arg, rt, 0);
}
else
throw 1;
}
else
{
if ((int)token.size() == 3)
{
argh = formatr(instr, token.at(0), token.at(2), token.at(3), linen);
assembled = rassemble(instr, argh[0], argh[1], argh[2], argh[3]);
}
else
throw 1;
}
}
catch (int e)
{
if (e == 1)
std::cerr << "Wrong number of arguments at line " << linen << std::endl;
}
}
else if (type == I)
{
try
{
if (instr == "lui")
{
if ((int)token.size() == 2)
{
argh = formati(instr, token.at(0), token.at(1), linen);
assembled = iassemble(instr, argh[0], 0, argh[1]);
}
else
throw 1;
}
else
{
if ((int)token.size() == 3)
{
argh = formati(instr, token.at(0), token.at(1), token.at(2), linen);
assembled = iassemble(instr, argh[0], argh[1], argh[2]);
}
else
throw 1;
}
}
catch (int e)
{
if (e == 1)
std::cout << "Wrong number of arguments at line " << linen << std::endl;
}
}
else
std::cerr << "Instruction not recognized at line " << linen << std::endl;
results.push_back(assembled);
}
void assembler::secAdd(void)
{
std::vector<int>::iterator iter = results.begin();
for (int i = 0; i < (int)reloctable.size(); i++)
{
for (unsigned int j = 0; j < symbtable.size(); j++)
{
if (reloctable.at(i).label == symbtable.at(j).label)
{
int assembled = 0;
iter += (reloctable.at(i).rlinenum / 4);
for (unsigned int k = 0; k < ops.size(); k++)
{
if (reloctable.at(i).instruct == ops.at(k).instruct)
type = I;
}
if (type == I)
assembled = iassemble(reloctable.at(i).instruct, reloctable.at(i).rt, reloctable.at(i).rs, symbtable.at(i).slinenum);
else
std::cerr << "Instruction not recognized at line " << reloctable.at(i).rlinenum << std::endl;
results.erase(iter);
results.insert(iter, assembled);
}
}
}
}
int assembler::rassemble(std::string instr, int rd, int rs, int rt, int shamt)
{
int func = 0;
int code = 0;
for (int i = 0; i < (int)functions.size(); i++)
{
if (instr == functions.at(i).instruct)
{
func = functions.at(i).funct;
break;
}
else
{
if (i == (functions.size() - 1))
return -1;
}
}
code = (rs << 21) + (rt << 16) + (rd << 11) + (shamt << 6) + func;
return code;
}
int assembler::iassemble(std::string instr, int rt, int rs, int imm)
{
int op = 0;
int code = 0;
for (int i = 0; i < (int)ops.size(); i++)
{
if (instr == ops.at(i).instruct)
{
op = ops.at(i).opc;
break;
}
else
{
if (i == (ops.size() - 1))
return -1;
}
}
code = (op << 26) + (rs << 21) + (rt << 16) + imm;
return code;
}
void assembler::p()
{
for (int i = 0; i < (int)results.size(); i++)
std::cout << results.at(i) << " ";
std::cout << std::endl;
}
When debugging, the tokens parameter triggers the error, and the this pointer in the vector code shows that the vector size changes to 0 at these lines:
#if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
What exactly is happening?
Sorry if my formatting is bad/wrong, etc., and please let me know if I should make any edits/provide more code.
Thanks in advance.
The error is caused by accessing by index vector element that does not exist.
In you case lexed[i] should be valid. So, the only possible issue may be with empty labels vector. Validate this vector before accessing its elements, for example
myassembler.add(lexed[i].labels.empty() ? "" : lexed[i].labels[0],
lexed[i].name, tokens, i);
Actually there is one more bug for very large lexed arrays when integer index may overflow. You should not cast result of .size() to int. Instead proper type should be used for i:
for (size_t i = 0; i < lexed.size(); i++)