// StartUp by Sebastian Roll
// 2010-09-15
// This program reads a config file line by line (filename = "StartUpConfig.txt").
// If a line begins with "wait", StartUp delays specified seconds (example: "wait 5").
// All other lines are interpreted as fully qualified paths to a program to start (example: "C:\WINDOWS\notepad.exe").
// Empty lines are ignored.
#define WIN32_LEAN_AND_MEAN // exclude rarely-used services from Windows headers
#define NOGDI // exclude GDI services from Windows headers
#include <windows.h> // Sleep()
#include <shellapi.h> // ShellExecute()
#include <stdio.h> // FILE, fopen(), fgets(), fclose(), frpintf(), sprintf()
#include <stdlib.h> // EXIT_SUCCESS, atoi(), malloc(), free()
#include <string.h> // strlen(), strncmp()
#include <limits.h> // INT_MAX
// the file to read the config from
#define FILE_NAME "StartUpConfig.txt"
// config file: the max size of one line to read AND the max amount of all lines
#define LINES_SIZE (1024)
// the text to interpret as "delay command" within the config file
#define WAIT_STRING "wait"
// the max "delay" we allow (4294967 seconds = 1193 hours)
#define WAIT_SECONDS_MAX (INT_MAX/1000)
// CarrierReturn ASCII code
#define CHAR_CODE_CARRIER_RETURN (0x0D)
// LineFeed ASCII code
#define CHAR_CODE_LINE_FEED (0x0A)
// the config file is stored to this 2D array
// each line (first[]) holds a command to execute (second[])
// e.g. config[2][0] is the first char of the 3rd command line
// e.g. &(config[2][0]) is the C-string of the 3rd command line
static char config[LINES_SIZE][LINES_SIZE];
// funtion prototypes
int getConfig();
int formatLine(int line, int lineLength);
char* getLine(int line);
void startProcess(int line);
void wait(int seconds);
void printShellExecuteError(int error);
// application entry point
int main(void)
{
int i = 0;
int numOfLines = getConfig();
int waitStringSize = strlen(WAIT_STRING);
for (i = 0; i < numOfLines; i++)
{
// check if we have a "delay command"
if (!strncmp(WAIT_STRING, getLine(i), waitStringSize))
{
wait(atoi(&(config[i][waitStringSize + 1])));
}
else
{
startProcess(i);
}
}
return EXIT_SUCCESS;
}
// reads file "FILE_NAME" and stores its content to config[][].
// skips empty lines in the file.
// removes LF/CR chars at the end of each line.
int getConfig()
{
int lineCount = 0;
int lineLength = 0;
int errorFlag = 0;
char* status = 0;
FILE* file = fopen(FILE_NAME, "r");
printf("getConfig() reading file %s \n", FILE_NAME);
if (NULL != file)
{
while (!feof(file))
{
// read a line from file and write to config[][]
status = fgets(getLine(lineCount), LINES_SIZE, file);
if ( (lineCount == 0) && (status == 0) )
{
errorFlag = 1;
printf("getConfig() read file error (empty file?): line %d \n", lineCount);
// exit while()
break;
}
// check size of the line
lineLength = strlen(getLine(lineCount));
if (lineLength >= LINES_SIZE - 1)
{
errorFlag = 1;
printf("getConfig() read file error: string too long: line %d \n", lineCount);
// exit while()
break;
}
// check if line is empty and
// check line for CarrierReturn/LineFeed chars
lineCount += formatLine(lineCount, lineLength);
if (lineCount >= LINES_SIZE)
{
errorFlag = 1;
printf("getConfig() file too long (max %d lines allowed) \n", LINES_SIZE);
// exit while()
break;
}
}
fclose(file);
if (errorFlag == 0)
{
printf("getConfig() found %d commands \n", lineCount);
}
}
else
{
printf("getConfig() cannot open file \n");
}
if (errorFlag == 1)
{
lineCount = 0;
}
return lineCount;
}
// overwrites last CR/LF char of given line with 0 (zero)
// returns 1 if given line is a valid command
// returns 0 if given line is emtpy
// returns 0 if given line consists of only one CR/LF char
int formatLine(int line, int lineLength)
{
int result = 0;
if (lineLength == 0)
{
// line is empty
}
else if (lineLength == 1)
{
// this line has either one LF/CR char or a valid filename with only one char
// suppress LF/CR char
if ( (config[line][lineLength - 1] == CHAR_CODE_CARRIER_RETURN) ||
(config[line][lineLength - 1] == CHAR_CODE_LINE_FEED) )
{
config[line][lineLength - 1] = 0;
}
else
{
// it is a valid filename with only one char
result = 1;
}
}
else
{
// suppress LF/CR char
if ( (config[line][lineLength - 1] == CHAR_CODE_CARRIER_RETURN) ||
(config[line][lineLength - 1] == CHAR_CODE_LINE_FEED) )
{
config[line][lineLength - 1] = 0;
}
// it is a valid filename with some chars
result = 1;
}
return result;
}
// returns the pointer to the first char of given line
char* getLine(int line)
{
return &(config[line][0]);
}
// executes the given line of the config file
void startProcess(int line)
{
HINSTANCE hInst;
printf("startProcess() \"%s\" \n", getLine(line));
hInst = ShellExecute(0, "open", // operation to perform
getLine(line), // application name
0, // additional parameters
0, // default directory
SW_SHOW);
// MSDN says: return code > 31 is "no error"
if ((int)hInst < 32)
{
printShellExecuteError((int)hInst);
}
}
// delays execution for given seconds
void wait(int seconds)
{
if ( (seconds > 0) && (seconds < WAIT_SECONDS_MAX) )
{
printf("wait() %d seconds ", seconds);
Sleep(seconds * 1000);
printf("end \n");
}
else
{
printf("wait() warning: cannot wait for %d seconds (invalid number?)\n", seconds);
}
}
// maps ShellExecute() error codes to text
void printShellExecuteError(int error)
{
char* standardText = "startProcess() error";
char* detailText = (char*)malloc(64);
switch (error)
{
case ERROR_FILE_NOT_FOUND:
detailText = "file not found";
break;
case ERROR_PATH_NOT_FOUND:
detailText = "path not found";
break;
case ERROR_BAD_FORMAT:
detailText = "not an executable";
break;
case SE_ERR_NOASSOC:
detailText = "filetype unknown";
break;
case SE_ERR_ACCESSDENIED:
detailText = "access denied";
break;
case SE_ERR_SHARE:
detailText = "sharing violation";
break;
default:
sprintf(detailText, "%d", error);
break;
}
printf("%s: %s \n", standardText, detailText);
free(detailText);
}