"Colors" - A Palm Project Example
Jan Schaumann <jschauma@netmeister.org>
Abstract
A brief walkthrough of the development of a simple program for the
Palm OS.
Table of contents
1. Introduction
2. Overview
3. "Colors" - A Palm Project Example
3.1 Project Definition/Specifications
3.2 Project Design/Layout
3.3 Resource Definitions
3.4 Main Program
3.5 Additional Routines and Procedures
3.6 Makefile
4. The Code
4.1 Callback.h
4.2 Colors.c
4.3 Colors.h
4.4 Colors.rcp
4.5 ColorsGeneral.c
4.6 ColorsRsc.h
4.7 EventHandlers.c
4.8 Hex2rgb.c
4.8.1 Plain C code for hex2rgb
4.9 Makefile
4.10 Rgb2hex
4.10.1 Plain C code for rgb2hex
1 Introduction
This document shows and explains the different stages of the development
of a program for the Palm OS Platform. It is assumed that you are
working in a Linux/Unix environment and have successfully installed
and familiarized yourself with the necessary tools (gcc, pilrc, POSE
etc). If you haven't done so, please read the following two documents:
* PalmMisc.pdf - available from http://www.netmeister.org/palm/PalmMisc.pdf
* POSE-HOWTO - available from http://www.netmeister.org/palm/POSE-HOWTO.pdf
2 Overview
We will create a simple application called "Colors", which will allow
the user to convert the Red-, Green- and Blue-Values of a color into
its hexadecimal value and vice versa. The development will proceed
in the following steps:
1. Project Definition/Specifications
2. Project Design/Layout
3. Resource Definitions
4. Main Program
5. Additional routines and procedures
6. Makefile
7. Testing and Debuggin
These are only the main steps and everybody who has done any software
development knows that a lot of other steps are involved, and that
"Testing and Debuggin" will take place at virtually every step of
the project.
3 "Colors" - A Palm Project Example
3.1 Project Definition/Specifications
It's important to state the aim of the application at the beginning,
no matter if you are writing the application for somebody else or
just for fun. By thinking about what the program needs and how it
is organized, what features the user might want, your project will
get much more organized and thus easier to develop. These specifications
also facilitate splitting up the project among different devlopers.
Since this is a very simple example, you might think that this step
is not neccessary, but let's do it anyway for educational purposes:
The application "Colors" will allow the user to convert the Red-, Green-
and Blue-Values of a color into its hexadecimal value and vice versa.
It should have a menubar containing the items "Edit" ("Cut", "Copy",
"Paste") and "Options" ("About Colors"). Each command should have
a graffiti shortcut character. By keeping the layout of our application
close to other Palm-Applications, we ensure that the user feels familiar
with it. We want to be able to cut, copy and paste from/to the clipboard
by using the menu or the graffiti shortcuts; the other menu-item will
pop up a small alert with useful (?) information about the program.
When the user leaves the application, we want to store his/her latest
result in a database and show it when the application is opened the
next time. We also need a small error-alert in case the user enters
invalid characters.
3.2 Project Design/Layout
We now need to think about what the program interface will look like.
From the specifications we know that we need the menu and editable
text-fields for the Red-, Green, Blue-, and Hex-value of the color.
We then want two buttons, one which will convert from hexadecimal
to RGB and one which converts from RGB to hexadecimal. Note that there
are of course many other ways of designing the program. Our interface
might look something like this:
Menu
Red: ___
Green: ___
Blue: ___
From RGB From Hex
HexValue: ___
We do not want a frame around the form, so we want to specify the size
of the form to be 160x160.
3.3 Resource Definitions
Designing the layout and then transferring this layout into code is
very easy. All you need to do is create a text file containing the
specifications, and a resource-header which will define the ID for
each element. (You can have pilrc assign the ID's automatically, but
it will be easier for you lateron in the code if you give a name to
each element.) Here is what our "Colors.rcp" file will look like:
#include "ColorsRsc.h"
MENU ID ColorsMainMenuBar
BEGIN
PULLDOWN "Edit"
BEGIN
MENUITEM "Cut " ID Cut "X"
MENUITEM "Copy " ID Copy "C"
MENUITEM "Paste " ID Paste "P"
END
PULLDOWN "Options"
BEGIN
MENUITEM "About Colors " ID Info "A"
END
END
FORM ID ColorsForm AT (0 0 160 160)
MENUID 1000
BEGIN
TITLE "Palm Color Converter"
LABEL "Red: " AUTOID AT (10 30)
FIELD ID ColorsFormFieldRed AT (75 PrevTop 15 15) RIGHTALIGN FONT
0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
LABEL "Green: " AUTOID AT (10 PrevBottom+5)
FIELD ID ColorsFormFieldGreen AT (75 PrevTop 15 15) RIGHTALIGN
FONT 0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
LABEL "Blue: " AUTOID AT (10 PrevBottom+5)
FIELD ID ColorsFormFieldBlue AT (75 PrevTop 15 15) RIGHTALIGN FONT
0 UNDERLINED SINGLELINE MAXCHARS 3 NUMERIC
BUTTON "From RGB" ID ColorsFormButtonFromRgb AT (20 PrevBottom+10
AUTO AUTO)
BUTTON "From Hex" ID ColorsFormButtonFromHex AT (PrevRight+20 PrevTop
AUTO AUTO)
LABEL "Hexvalue: " AUTOID AT (10 PrevBottom+20)
FIELD ID ColorsFormFieldHex AT (75 PrevTop 32 15) RIGHTALIGN FONT
0 UNDERLINED SINGLELINE MAXCHARS 6
END
ALERT ID AboutAlert
INFORMATION
BEGIN
TITLE "About Colors"
MESSAGE "The Palm Color Converter lets you convert the Red-, Green-
and Blue values of a color into it's hexadecimal value and vice versa."
BUTTONS "OK"
ALERT ID TheError
INFORMATION
BEGIN
TITLE "Error"
MESSAGE "^1"
BUTTONS "OK"
END
And that's all! The IDs are defined in the included headerfile "ColorsRsc.h":
// ColorsRsc.h
// defines constants for all the applications resources
//
// App Name: "Colors"
//
// App Version: "0.1"
// Main Form
#define ColorsForm 1000
// main menu
#define ColorsMainMenuBar 1000
#define Cut 1001
#define Copy 1002
#define Paste 1003
#define Info 1004
// fields and buttons
#define ColorsFormFieldRed 1010
#define ColorsFormFieldGreen 1011
#define ColorsFormFieldBlue 1012
#define ColorsFormButtonFromRgb 1013
#define ColorsFormButtonFromHex 1014
#define ColorsFormFieldHex 1015
// Alerts
#define AboutAlert 2000
#define TheError 3000
As always, it's good practice to comment your code. Once you've written
"Colors.rcp" and "ColorsRcp.h", you can preview your layout using
pilrcui Colors.rcp:
3.4 Main Program
Now it's time to write the main function (PilotMain), which is standard:
DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags) {
Err err = 0;
if (launchCode == sysAppLaunchCmdNormalLaunch) {
if ((err = StartApplication()) == 0) {
EventLoop();
StopApplication();
}
}
return err;
}
For this simple application we only check if it is launched via the
standard launch code, we ignore all other possible ways of launching
the application. StartApplication is standard as well - it just calls
OpenDatabase and calls FrmGotoForm(ColorsForm) to display our main
(and only) form. OpenDatabase creates the database for our application
if it doesn't exist, and creates record zero containing a null string.
If all this took place without any errors, EventLoop is entered (which
will wait for events triggered by the users actions) and is left only
when interrupted by the systems call to stop the application. If this
is the case, we call StopApplication which will write the necessary
data into the database. You can review the complete code for "Colors"
at the end of this document.
3.5 Additional Routines and Procedures.
In order for our form to process (handle) any incoming events, we need
to write a Form Handler, which executes certain pieces of code according
to the events we want to handle. Following is the function ColorsFormHandleEvent
in parts:
static Boolean ColorsFormHandleEvent(EventPtr event) {
//initialization of local variables
#ifdef __GNUC__
CALLBACK_PROLOGUE
#endif
index = 0;
theForm = FrmGetActiveForm();
switch (event->eType) {
case frmOpenEvent:
FrmDrawForm(theForm);
// here we also read the last record from the
// database and insert it into the appropriate field
}
handled = true;
break;
case ctlSelectEvent:
// here we handle the two buttons
objID = event->data.ctlSelect.controlID;
if (objID == ColorsFormButtonFromRgb) {
convertFromRgb();
}
else {
convertFromHex();
}
handled = true;
break;
case menuEvent:
// here we handle the menu-events
handled = true;
}
#ifdef __GNUC__
CALLBACK_EPILOGUE
#endif
return handled;
}
Note the inclusion of CALLBACK_PROLOGUE and CALLBACK_EPILOGUE. We need
these since we are using GCC to compile the code. The GCC compiler's
calling conventions differ from those in the Palm OS. In particular,
the GCC compiler expects at startup that it can set up the A4 register
(which it uses to access global variables) and that it will remain
set throughout the life of the application. Unfortunately, this is
not true when a GCC application calls a Palm OS routine that either
directly or indirectly calls back to a GCC function. This can lead
to spectacular applications crashes. Refer to the complete code at
the end of this document to see what CALLBACK_PROLOGUE and CALLBACK_EPILOGUE
do.
This function calls convertFromRgb or convertFromHex, the functions
actually responsible for the conversion, depending on which button
the user taps.
3.6 Makefile
Once we've written all the code, we need to compile and link it. This
may not be very complicated for a small project like this, but for
bigger projects this would be tedious work. I will not go into the
details of Makefiles, see http://www.tw.gnu.org/software/make/make.html
for further information. Here's the Makefile I used for our little
project:
APP =Colors
ICONTEXT ="Colors"
APPID =WRLD
RCP =$(APP).rcp
PRC =$(APP).prc
SRC =$(APP).c
GRC =$(APP).grc
BIN =$(APP).bin
CC =m68k-palmos-coff-gcc
PILRC =pilrc
TXT2BITM =txt2bitm
OBJRES =m68k-palmos-coff-obj-res
BUILDPRC =build-prc
CFLAGS =-O0 -g $(DEFINES) $(INCLUDES)
all: clean $(PRC)
$(PRC): grc.stamp bin.stamp;
$(BUILDPRC) $(PRC) $(ICONTEXT) $(APPID) *.grc *.bin $(LINKFILES)
ls -l *.prc
grc.stamp: $(APP) ;
$(OBJRES) $(APP)
touch $@
$(APP): $(SRC:.c=.o) ;
$(CC) $(CFLAGS) $^ -o $@
bin.stamp: $(RCP) ;
$(PILRC) $^ $(BINDIR)
touch $@
%.o: %.c ;
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf *.o $(APP) *.bin *.grc *.prc *.stamp
Note that I always "make clean", since I split up the functions over
several files. There are other (maybe more elegant) possibilities
to do this with Makefiles and I won't try to argue that this method
is better. Once compilation is complete, you will find the file "Colors.prc"
in your directory - this is the application ready to install on your
Palm (or POSE).
4 The Code
Following is the code for the following files:
* Callbacks.h
* Colors.c
* Colors.h
* Colors.rcp
* ColorsGeneral.c
* ColorsRsc.h
* EventHandlers.c
* Hex2rgb.c
* Makefile
* Rgb2hex.c
4.1 Callbacks.h
#ifndef __CALLBACK_H__
#define __CALLBACK_H__
register void *reg_a4 asm("%a4");
#define CALLBACK_PROLOGUE \
void *save_a4 = reg_a4; asm("move.l %%a5,%%a4; sub.l #edata,%%a4"
: :);
#define CALLBACK_EPILOGUE reg_a4 = save_a4;
#endif
4.2 Colors.c
#include "Colors.h"
#include "Rgb2hex.c"
#include "Hex2rgb.c"
#include "EventHandlers.c"
#include "ColorsGeneral.c"
static void EventLoop(void) {
EventType event;
Word error;
do {
EvtGetEvent(&event, evtWaitForever);
if (!SysHandleEvent(&event)) {
if (!MenuHandleEvent(0, &event, &error)) {
if (!ApplicationHandleEvent(&event)){
FrmDispatchEvent(&event);
}
}
}
} while (event.eType != appStopEvent);
}
static Err OpenDatabase(void) {
Err err = 0;
UInt index = 0;
VoidHand RecHandle;
Ptr RecPointer;
char nullstring = 0;
myDB = DmOpenDatabaseByTypeCreator(myDBType, myAppID, dmModeReadWrite);
if (!myDB) {
if ((err = DmCreateDatabase(0, myDBName, myAppID, myDBType, false))!=0)
return err;
myDB = DmOpenDatabaseByTypeCreator(myDBType, myAppID, dmModeReadWrite);
RecHandle = DmNewRecord(myDB, &index, 1);
RecPointer = MemHandleLock(RecHandle);
DmWrite(RecPointer, 0, &nullstring, 1);
MemPtrUnlock(RecPointer);
DmReleaseRecord(myDB, index, true);
}
return 0;
}
static Err StartApplication(void) {
Err err = 0;
err = OpenDatabase();
if (err)
return err;
FrmGotoForm(ColorsForm);
return 0;
}
static Err StopApplication(void) {
VoidHand myRecord;
FormPtr theForm;
char *theRecord;
FieldPtr theField;
Ptr RecPointer;
UInt index = 0;
theForm = FrmGetActiveForm();
theField = FrmGetObjectPtr(theForm, FrmGetObjectIndex (theForm, ColorsFormFieldHex));
theRecord = theField->text;
if (theRecord!=NULL) {
DmRemoveRecord(myDB, index);
myRecord = DmNewRecord(myDB, &index, 1);
myRecord = DmResizeRecord(myDB, index, 6);
RecPointer = MemHandleLock(myRecord);
DmWrite(RecPointer, 0, theRecord, 6);
MemPtrUnlock(RecPointer);
DmReleaseRecord(myDB, index, true);
}
DmCloseDatabase(myDB);
}
DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags) {
Err err = 0;
if (launchCode == sysAppLaunchCmdNormalLaunch) {
if ((err = StartApplication()) == 0) {
EventLoop();
StopApplication();
}
}
return err;
}
4.3 Colors.h
#ifndef __COLORS_H
#define __COLORS_H
#include <Pilot.h>
#include <stdio.h>
#include <stdlib.h>
#include "ColorsRsc.h"
#ifdef __GNUC__
#include "Callbacks.h"
#endif
#define myAppID 'CLRS'
#define myDBType 'Data'
DmOpenRef myDB;
char myDBName[] = "ColorsDB";
static int myIsXDigit(char);
static void convertFromHex(void);
static void AlertError(char *);
static Boolean ColorsFormHandleEvent(EventPtr);
static Boolean ApplicationHandleEvent(EventPtr);
static void EventLoop(void);
static Err OpenDatabase(void);
static Err StartApplication(void);
static Err StopApplication(void);
DWord PilotMain(Word launchCode, Ptr cmdPBP, Word launchFlags);
static void convertFromRgb(void);
#endif
4.4 Colors.rcp
See Section 3.3
4.5 ColorsGeneral.c
#include "Colors.h"
static void AlertError(char *theMessage) {
FrmCustomAlert(TheError, theMessage, NULL, NULL);
}
4.6 ColorsRsc.h
See Section 3.3
4.7 EventHandlers.c
#include "Colors.h"
static Boolean ColorsFormHandleEvent(EventPtr event) {
Boolean handled = false;
int objID;
FormPtr theForm;
FieldPtr theField;
VoidHand myRecord;
UInt index;
Ptr RecPointer;
char *theRecord;
#ifdef __GNUC__
CALLBACK_PROLOGUE
#endif
index = 0;
theForm = FrmGetActiveForm();
switch (event->eType) {
case frmOpenEvent:
// get the record
myRecord = DmGetRecord(myDB, index);
theRecord = MemHandleLock(myRecord);
MemHandleUnlock(myRecord);
DmReleaseRecord(myDB, index, true);
FrmDrawForm(theForm);
if ((theRecord != NULL)&&(StrLen(theRecord)==6)) {
// we have a valid record, display it
FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm,
ColorsFormFieldHex)), theRecord, 6);
// and show the rgb - this way we don't need to store those!
convertFromHex();
}
handled = true;
break;
case ctlSelectEvent:
objID = event->data.ctlSelect.controlID;
if (objID == ColorsFormButtonFromRgb) {
convertFromRgb();
}
else {
convertFromHex();
}
handled = true;
break;
case menuEvent:
if (event->data.menu.itemID == Cut) {
// Cut
objID = FrmGetFocus(theForm);
FldCut(FrmGetObjectPtr(theForm, objID));
}
else if (event->data.menu.itemID == Copy) {
// Copy
objID = FrmGetFocus(theForm);
FldCopy(FrmGetObjectPtr(theForm, objID));
break;
}
else if (event->data.menu.itemID == Paste) {
// Paste
objID = FrmGetFocus(theForm);
FldPaste(FrmGetObjectPtr(theForm, objID));
break;
}
else if (event->data.menu.itemID == Info)
// display info
FrmAlert(AboutAlert);
handled = true;
}
#ifdef __GNUC__
CALLBACK_EPILOGUE
#endif
return handled;
}
static Boolean ApplicationHandleEvent(EventPtr event) {
FormPtr frm;
Int formId;
Boolean handled = false;
if (event->eType == frmLoadEvent) {
// Load the form resource specified in the event, then activate
it
formId = event->data.frmLoad.formID;
frm = FrmInitForm(formId);
FrmSetActiveForm(frm);
// Set the event Handler for the form. The handler of the currently
active form
// is called by FrmDispatchEvent each time it is called
switch (formId) {
case ColorsForm:
FrmSetEventHandler(frm, ColorsFormHandleEvent);
break;
}
handled = true;
}
return handled;
}
4.8 Hex2rgb.c
#include "Colors.h"
static int myIsXDigit(char c) {
if ((c=='0')||(c==NULL))
return 0;
if (c=='1')
return 1;
if (c=='2')
return 2;
if (c=='3')
return 3;
if (c=='4')
return 4;
if (c=='5')
return 5;
if (c=='6')
return 6;
if (c=='7')
return 7;
if (c=='8')
return 8;
if (c=='9')
return 9;
if ((c=='a')||(c=='A'))
return 10;
if ((c=='b')||(c=='B'))
return 11;
if ((c=='c')||(c=='C'))
return 12;
if ((c=='d')||(c=='D'))
return 13;
if ((c=='e')||(c=='E'))
return 14;
if ((c=='f')||(c=='F'))
return 15;
return -1;
}
static void convertFromHex(void) {
int i;
char *FldHex;
int flag=0;
FormPtr theForm;
char hex[6] = {'0','0','0','0','0','0'};
int tmp[3] = {0,0,0};
theForm = FrmGetActiveForm();
// delete old stuff
FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldRed)),
0, 3);
FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldGreen)),
0, 3);
FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldBlue)),
0, 3);
// get input
FldHex = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm,
ColorsFormFieldHex)));
// check if input is correct
if (FldHex!=NULL) {
for (i=0; i<6; i++) {
// check if input is correct
if (!flag){
hex[i] = FldHex[i];
if (myIsXDigit(hex[i])<0) {
AlertError("Incorrect Input!");
flag = 1;
}
}
}
if (StrLen(hex)<6) {
AlertError("Not enough characters!");
flag = 1;
}
if (!flag) {
for (i=0;i<3;i++) {
tmp[i] = 0;
tmp[i] += myIsXDigit(hex[i*2])*16;
tmp[i] += myIsXDigit(hex[i*2+1]);
}
}
}
else {
for (i=0;i<3;i++)
tmp[i]=0;
}
if (!flag) {
FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldRed)),
StrIToA(hex,tmp[0]), StrLen(StrIToA(hex, tmp[0])));
FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldGreen)),
StrIToA(hex,tmp[1]), StrLen(StrIToA(hex, tmp[1])));
FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldBlue)),
StrIToA(hex,tmp[2]), StrLen(StrIToA(hex, tmp[2])));
}
}
Note that the much more elegant way to convert the value in plain C
as shown below will not work, as the printf -functions are implemented
in a different way, just as filestreams are not as easy as in plain
C.
4.8.1 Plain C code for hex2rgb
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *color[] = { "Red:", "Green:", "Blue:" };
void error( const char *str ) {
fprintf(stderr, "%s\n", str);
exit(1);
}
int main(int argc, char *argv[]) {
int i;
unsigned long l;
char *input = argv[1];
if(argc != 2)
error("Usage: hex2rgb xxxxxx, where x=[a-fA-F0-9]");
if(strlen(input) != 6)
error("The hexadecimal value of a color must have 6 characters!");
for( i = 0; i < strlen( input ); i++ )
if( !isxdigit( input[i] ) )
error( "Input is not a valid hexnumber" );
i = sscanf(input, "%6lx", &l);
if(i <= 0)
error("Error in hexvalue!");
for(i = 0; i<3; i++) {
fprintf(stdout, "%d ", (int)( l & 0xFF0000 ) >> 16 );
l <<= 8;
}
fprintf(stdout, "\n");
return 0;
}
4.9 Makefile
See Section 3.6
4.10 Rgb2hex
#include "Colors.h"
static void convertFromRgb(void) {
char *FldRed;
char *FldGreen;
char *FldBlue;
int flag=0;
FormPtr theForm;
char tmp[8];
char result[6] = {'0','0','0','0','0','0'};
theForm = FrmGetActiveForm();
// delete old entry
FldDelete(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)),
0, 6);
// get the input
FldRed = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm,
ColorsFormFieldRed)));
FldGreen = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm,
ColorsFormFieldGreen)));
FldBlue = FldGetTextPtr(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm,
ColorsFormFieldBlue)));
// check if input is correct
if ((FldRed!=NULL)&&(atoi(FldRed)>255)) {
AlertError("Incorrect input!");
flag = 1;
}
if ((FldGreen!=NULL)&&(atoi(FldGreen)>255)) {
AlertError("Incorrect input!");
flag = 1;
}
if ((FldBlue!=NULL)&&(atoi(FldBlue)>255)) {
AlertError("Incorrect input!");
flag = 1;
}
// all's swell, we go on
if (!flag) {
if (FldRed == NULL) {
result[0] = '0';
result[1] = '0';
}
else {
StrIToH(tmp, atoi(FldRed));
result[0] = tmp[6];
result[1] = tmp[7];
}
if (FldGreen == NULL) {
result[2] = '0';
result[3] = '0';
}
else {
StrIToH(tmp, atoi(FldGreen));
result[2] = tmp[6];
result[3] = tmp[7];
}
if (FldBlue == NULL) {
result[4] = '0';
result[5] = '0';
}
else {
StrIToH(tmp, atoi(FldBlue));
result[4] = tmp[6];
result[5] = tmp[7];
}
result[6] = '\0';
// write the correct hexvalue into the appropriate field
FldInsert(FrmGetObjectPtr(theForm, FrmGetObjectIndex(theForm, ColorsFormFieldHex)),
result, 6);
}
}
4.10.1 Plain C code for rgb2hex
As before, you would implement this function in a slightly different
way if using plain C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void error( const char *str ) {
fprintf( stderr, "%s\n", str );
exit(1);
}
int main(int argc, char **argv) {
char *input[3];
int i;
if (argc!=4) {
error("Usage: rgb2hex R G B (where 0 >= R,G,B (integer) <= 255)");
exit(1);
}
for (i=0; i<3; i++) {
input[i] = argv[i+1];
}
for (i=0; i<strlen(input[0]); i++) {
if (!isdigit(input[0][i])) {
error("Incorrect input!");
exit(1);
}
}
for (i=0; i<strlen(input[1]); i++) {
if (!isdigit(input[1][i])) {
error("Incorrect input!");
exit(1);
}
if ((atoi(input[0])>255)||(atoi(input[1])>255)||(atoi(input[2])>255)) {
error("Incorret input!");
exit(1);
}
fprintf(stdout,"%02x%02x%02x\n", atoi(input[0]), atoi(input[1]), atoi(input[2]));
}