Miscellaneous C functions and source code
By Frank Cox
(December 12, 2009)
Most of these functions consist of self-contained “demo programs” that can be compiled as-is for testing, then you can rip out any parts that you want to use in own programs.
These programs were written on Centos 5 and most require ncurses, which is installed by default on most Linux distributions. You should be able to compile them as-is on pretty much any modern Linux or Unix system. I really don't know what it would take to get this stuff working on Microsoft Windows, though there does appear to be a MS Windows compatible version of ncurses called pdcurses which might work.
All programs on this web page are subject to the following copyright notice:
Copyright (c) 2009, Frank Cox All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY FRANK COX ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FRANK COX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PERCENT BAR
A moving progress bar that can be used to count up or down while doing things like reading datafiles so the program user can see that something is happening.
This function is based on a subroutine written by James Goldbloom in 1996. You can download the original here.
#include <ncurses.h> #include <string.h> #include <stdbool.h> void percentbar(const int value, const int high, const int y, const int x, int width, const int fg, const int bg, const int highlight, const bool showvalues); int main(void) { initscr(); start_color(); percentbar(50,100,21,0,73,11,6,15,true); getch(); endwin(); return 0; } void percentbar(const int value, const int high, const int y, const int x, int width, const int fg, const int bg, const int highlight, const bool showvalues) //Example: percentbar(50,100,21,0,73,11,6,15,true); <-- Display a 50% bar at bottom of screen { int oldx, oldy, percentage,counter; attr_t attributes; // ncurses text attributes short cpair; // ncurses color pair (not using this one at the moment) char tempbar[20]; getyx(stdscr,oldy,oldx); attr_get(&attributes,&cpair,NULL); //get current text attributes before we start if (width > 73) width = 73; percentage = value*100/high; if (fg < 8) { init_pair(63,fg,bg); attrset(COLOR_PAIR(63)); } else { init_pair(63,fg-8,bg); attrset(COLOR_PAIR(63)|A_BOLD); } move(y,x); attron(A_ALTCHARSET); if (showvalues) { sprintf(tempbar,"%d/%d",value,high); addch(ACS_ULCORNER); for (counter=1; counter < width/2; counter++) addch(ACS_HLINE); addch(ACS_RTEE); attroff(A_ALTCHARSET); if (highlight < 8) { init_pair(62,highlight,bg); attrset(COLOR_PAIR(62)); } else { init_pair(62,highlight-8,bg); attrset(COLOR_PAIR(62)|A_BOLD); } addstr(tempbar); if (fg < 8) attrset(COLOR_PAIR(63)); else attrset(COLOR_PAIR(63)|A_BOLD); attron(A_ALTCHARSET); addch(ACS_LTEE); for (counter=0; counter < width-(width/2+strlen(tempbar))+4; counter++) addch(ACS_HLINE); addch(ACS_URCORNER); } else { addch(ACS_ULCORNER); for (counter=-5; counter < width; counter++ ) addch(ACS_HLINE); addch(ACS_URCORNER); } move(y+1,x); addch(ACS_VLINE); attroff(A_ALTCHARSET); if (highlight < 8) attrset(COLOR_PAIR(62)); else attrset(COLOR_PAIR(62)|A_BOLD); printw("%d%%",percentage); sprintf(tempbar,"%d",percentage); for (counter=1; counter < 5-strlen(tempbar); counter++) addch(' '); move(y+1,x+6); if (fg < 8) attrset(COLOR_PAIR(63)); else attrset(COLOR_PAIR(63)|A_BOLD); for (counter=0; counter < width*percentage/100; counter++) addch(ACS_DIAMOND); if (width-width*percentage/100 != 1) { attron(A_ALTCHARSET); for (counter=0; counter < width-width*percentage/100; counter++) addch(ACS_BULLET); addch(ACS_VLINE); } move(y+2,x); addch(ACS_LLCORNER); for (counter=-5; counter < width; counter++) addch(ACS_HLINE); addch(ACS_LRCORNER); attroff(A_ALTCHARSET); attrset(attributes); //restore text attributes attributes move(oldx,oldy); // restore cursor location }
POSIX FILE LOCKING FUNCTIONS
A function to open and close a file using POSIX file locking.
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> FILE* openfile(const char *filename, const char *operation); void closefile(FILE*); int main(void) { FILE *fp; char name[20]; fp=openfile("text.txt","a"); printf("Hit a key to continue\n"); scanf("%s", name); closefile(fp); return 0; } FILE* openfile(const char *filename, const char *operation) { struct flock fl; FILE *file; if (strcmp(operation,"r") == 0) fl.l_type = F_RDLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */ else fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */ fl.l_start = 0; /* Offset from l_whence */ fl.l_len = 0; /* length, 0 = to EOF */ fl.l_pid = getpid(); /* our PID */ if ((file = fopen(filename,operation)) == NULL) { printf(" can't open %s\n", filename); exit(1); } if (fcntl(fileno(file), F_SETLKW, &fl) == -1) { printf("can't set lock %s\n",filename); exit(1); } return file; } void closefile(FILE *file) { struct flock fl; fl.l_type = F_UNLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */ fl.l_whence = SEEK_SET; /* SEEK_SET, SEEK_CUR, SEEK_END */ fl.l_start = 0; /* Offset from l_whence */ fl.l_len = 0; /* length, 0 = to EOF */ fl.l_pid = getpid(); /* our PID */ fflush(file); // flush data before releasing lock if (fcntl(fileno(file), F_SETLK, &fl) == -1) { printf("can't release file lock.\n"); exit(1); } fclose(file); return; }
TEXT EDITOR
A simple multi-line text editor. As presented here, this text editor converts the return key to a space, you must hit tab to exit the function, and the editor will reject the < and > keys. It would be simple to change this behaviour to translate or reject any keys that you wish and to exit the function with any key you choose.
#include <ncurses.h> #include <string.h> #include <stdbool.h> //string length for word wrap in ad text editor #define WORDWRAPLENGTH 76 bool texteditor(char *text); void left(char *string, const int length); void right(char *string, const int length); int main(void) { initscr(); noecho(); cbreak(); keypad(stdscr,TRUE); start_color(); char adtext[256]="\0"; if (texteditor(adtext)) ; else strcpy(adtext,"Esc hit"); clear(); addstr(adtext); getch(); endwin(); return(0); } bool texteditor(char *text) { int counter, counter2, ky=0; int csrpos=0; char temp[256]; char wrappedtext[4][WORDWRAPLENGTH+1]; bool ins=false; for (counter=strlen(text); counter < 256; counter++) text[counter]='\0'; while (ky != 9) // repeat until we hit the tab key { for (counter=0; counter < 4; counter++) wrappedtext[counter][0] = '\0'; for (counter=0; counter < 4; counter++) { strcpy(temp,text); right(temp,strlen(temp)-strlen(wrappedtext[0])-strlen(wrappedtext[1])-strlen(wrappedtext[2])-counter); if (temp[0] =='\0') break; left(temp,WORDWRAPLENGTH); if (strchr(temp,' ') == NULL || strlen(temp) < WORDWRAPLENGTH) { strcpy(wrappedtext[counter],temp); break; } else { left(temp,strrchr(temp,' ')-temp); strcpy(wrappedtext[counter],temp); } } for (counter=0; counter < 4; counter++) { move(counter+4,1); for (counter2=0; counter2 < 76; counter2++) addch(' '); mvaddstr(counter+4,1,wrappedtext[counter]); } // put the cursor on the screen if (strlen(text) > 0) { if (csrpos < strlen(wrappedtext[0])+2) move(4,csrpos+1); else if (csrpos < strlen(wrappedtext[0])+strlen(wrappedtext[1])+3) move(5,csrpos-strlen(wrappedtext[0])); else if (csrpos < strlen(wrappedtext[0])+strlen(wrappedtext[1])+strlen(wrappedtext[2])+4) move(6,csrpos-strlen(wrappedtext[0])-strlen(wrappedtext[1])-1); else move(7,csrpos-1-strlen(wrappedtext[0])-strlen(wrappedtext[1])-strlen(wrappedtext[2])-1); } else move(4,1); refresh(); ky=getch(); switch (ky) { case KEY_HOME: csrpos =0; break; case KEY_UP: if (csrpos < strlen(wrappedtext[0])+2) ; // do nothing else if (csrpos < strlen(wrappedtext[0])+strlen(wrappedtext[1])+3) csrpos=csrpos-strlen(wrappedtext[0])-1; else if (csrpos < strlen(wrappedtext[0])+strlen(wrappedtext[1])+strlen(wrappedtext[2])+4) csrpos=csrpos-strlen(wrappedtext[1])-1; else csrpos=csrpos-strlen(wrappedtext[2])-1; break; case KEY_LEFT: if (csrpos > 0) csrpos--; break; case KEY_RIGHT: if (csrpos <= strlen(text)) csrpos++; break; case KEY_END: csrpos=strlen(text); break; case KEY_DOWN: if (csrpos< strlen(wrappedtext[0]) +2) { if (csrpos + strlen(wrappedtext[0]) < strlen(text)) csrpos=csrpos + strlen(wrappedtext[0])+1; } else if (csrpos< strlen(wrappedtext[0])+strlen(wrappedtext[1]) +3) { if (csrpos + strlen(wrappedtext[1]) < strlen(text)) csrpos= csrpos+ strlen(wrappedtext[1])+1; } else if (csrpos < strlen(wrappedtext[0])+strlen(wrappedtext[1]) + strlen(wrappedtext[2]) +4) { if (csrpos + strlen(wrappedtext[2]) < strlen(text)) csrpos=csrpos+strlen(wrappedtext[2])+1; else csrpos=strlen(text)+1; } break; case KEY_IC: //insert key ins = !ins; if (ins) { mvaddstr(15,1,"ins"); curs_set(2); } else { mvaddstr(15,1," "); curs_set(1); } break; case KEY_DC: //delete key if (csrpos > strlen(text)-1) break; for (counter=csrpos+1;text[counter] != '\0'; counter++) text[counter-1]=text[counter]; text[counter-1]='\0'; break; case 9: // tab //if the entry variable is 9 (tab) we can exit this function break; case KEY_BACKSPACE: if (csrpos > 0) { for (counter=csrpos; text[counter] != '\0'; counter++) text[counter-1]=text[counter]; text[counter-1]='\0'; csrpos--; } break; case '<': // don't accept <> in the editor case '>': break; case 27: //esc // esc twice to get out, otherwise eat the chars that don't work //from home or end on the keypad ky=getch(); if (ky == 27) return (false); else if (ky=='[') { getch(); getch(); } else ungetch(ky); break; default: if (ky ==10) ky = ' '; //convert return to a space if (ky > 31 && ky < 126 && strlen(text) < 255 && strlen(wrappedtext[3]) < WORDWRAPLENGTH) { if (ins) memmove(text+csrpos+1,text+csrpos,255-csrpos); text[csrpos]=ky; if (csrpos < 255) ++csrpos; } } } return(true); } void left(char *string, const int length) { if (strlen(string) > length) string[length]='\0'; return; } void right(char *string, const int length) { if (strlen(string) > length) memmove(string, string+strlen(string)-length,length+1); return; }
EDITABLE SINGLE-LINE INPUT FIELD
You can specify what characters are allowed in the field. The function returns a value which is dependent on how the function was exited.
This function is based on a subroutine written by David Zarnitzky in 1992. You can download the original here.
#include <ncurses.h> #include <string.h> #include <stdbool.h> #define MAXIMUMUPUTSTRINGLENGTH 80 int uput(const int y,const int x,const int length,const int fg,const int bg,char *whole,bool ins,const char *permitted); char* rtrim(char* string, char junk); void insert(char *, const char*, const int); int main(void) { char whole[MAXIMUMUPUTSTRINGLENGTH]; int returnvalue; initscr(); cbreak(); noecho(); keypad(stdscr,TRUE); start_color(); move(7,6); addstr("1234567890"); refresh(); strcpy(whole,"abcd"); returnvalue=uput(6,6,6,0,7,whole,false,"123456789/*-+=()"); move(20,30); printw("returnvalue=%d whole=%s\n",returnvalue,whole); refresh(); flushinp(); getch(); endwin(); return 0; } int uput(const int y,const int x,const int length,const int fg,const int bg,char *whole,bool ins,const char *permitted) { /* +------------------------[ WHAT YOU PUT IN ]-------------------------------+ |UPUT(y, x, length, fg, bg, whole, ins, permitted) | +--------------------------------------------------------------------------+ |y -> Row where INPUT will start | |x -> Column where INPUT will start | |length -> Maximum length of INPUT | |fg -> Foreground color of INPUT line | |bg -> Background color of INPUT line | |whole -> String to be edited | |ins -> TRUE or FALSE for INSERT on/off | | permitted$ = the only valid characters | +---------------------[ WHAT YOU GET BACK ]--------------------------------+ | | | If UPUT is exited by the user pressing ESCAPE, then the FUNCTION will | | return the original string it was given (ie: no changes are made). If | | UPUT is exited any other way (TAB, SHIFT-TAB, UP, DOWN, ENTER), then | | the edited string is returned. | | | | In either case, the SHARED variable "keyflag%" is returned with a value | | which is dependent on HOW UPUT was exited, following the chart below | | +-----------------------------------+ | ESCAPE -> keyflag = 5 |The values are based on the KEYPAD!| | ENTER -> keyflag = 0 +--------------+--------+-----------+ | UP ARROW -> keyflag = 8 | (7) | UP(8) | (9) | | DOWN ARROW -> keyflag = 2 +--------------+--------+-----------+ | TAB -> keyflag = 6 | SHFT-TAB(4)| ESC(5) |TAB(6) | | SHIFT-TAB -> keyflag = 4 +--------------+--------+-----------+ | | (1) | DOWN(2)| (3) | | +--------------+--------+-----------| | | ENTER(0) | | +--------------------------------------+-----------------------------------+ Requires rtrim() and insert() functions -------------- NCURSES BASIC COLORS -------------- 0 Black COLOR_BLACK (DOS Color #0) 1 Red COLOR_RED (DOS Color #4) 2 Green COLOR_GREEN (DOS Color #2) 3 Brown COLOR_YELLOW (DOS Color #6) 4 Blue COLOR_BLUE (DOS Color #1) 5 Magenta COLOR_MAGENTA (DOS Color #5) 6 Cyan COLOR_CYAN (DOS Color #3) 7 White COLOR_WHITE (DOS Color #7) ------------------------------------------------ COLORS WITH BOLD ATTRIBUTE SET (Foreground only) ------------------------------------------------ 0 Gray (DOS Color #8) 1 Light Red (DOS Color #12) 2 Light Green (DOS Color #10) 3 Yellow (DOS Color #14) 4 Light Blue (DOS Color #9) 5 Light Magenta (DOS Color #13) 6 Light Cyan (DOS Color #11) 7 High Intensity White (DOS Color #15) */ int flag = 0, curspos=0,counter, ky; char tempwhole[MAXIMUMUPUTSTRINGLENGTH]; char kystring[2]; bool exitflag=false; kystring[1]='\0'; strcpy(tempwhole,whole); if (fg < 8) { init_pair(64,fg,bg); attrset(COLOR_PAIR(64)); } else { init_pair(64,fg-8,bg); attrset(COLOR_PAIR(64)|A_BOLD); } do { move(y,x); for (counter=0; counter < length; counter++) addch(' '); move (y,x); addstr(whole); move(y,x+curspos); if (ins) curs_set(2); else curs_set(1); refresh(); ky=getch(); switch (ky) { case KEY_LEFT: if (curspos != 0) curspos--; break; case KEY_RIGHT: if (curspos != length-1 && whole[curspos] != '\0') curspos++; break; case KEY_HOME: //case KEY_A1: =KEY_HOME on Linux so not required curspos = 0; break; case KEY_END: //case KEY_C1: =KEY_END on Linux so not required rtrim(whole,' '); curspos=strlen(whole); if (strlen(whole) == length) curspos--; break; case KEY_IC: //insert key ins = !ins; if (ins) curs_set(2); else curs_set(1); break; case KEY_DC: //delete key if (curspos > strlen(whole)-1) break; for (counter=curspos+1;whole[counter] != '\0'; counter++) whole[counter-1]=whole[counter]; whole[counter-1]='\0'; break; case KEY_BACKSPACE: if (curspos > 0) { for (counter=curspos; whole[counter] != '\0'; counter++) whole[counter-1]=whole[counter]; whole[counter-1]='\0'; curspos--; } break; case 10: // enter flag=0; exitflag=true; break; case KEY_UP: // up-arrow flag=8; exitflag=true; break; case KEY_DOWN: // down-arrow flag=2; exitflag=true; break; case 9: // tab flag=6; exitflag=true; break; case KEY_BTAB: // shift-tab flag=4; exitflag=true; break; case 27: //esc // esc twice to get out, otherwise eat the chars that don't work //from home or end on the keypad ky=getch(); if (ky == 27) { strcpy(whole,tempwhole); flag=5; exitflag=true; } else if (ky=='[') { getch(); getch(); } else ungetch(ky); break; default: if (strchr(permitted,ky)) { kystring[0]=ky; if (ins) { if (curspos < strlen(whole)) { if (strlen(whole) < length) insert(whole,kystring,curspos); else curspos--; } else strcat(whole,kystring); } else if (curspos < strlen(whole)) whole[curspos]=ky; else strcat(whole,kystring); if (curspos < length-1) ++curspos; } } } while (!exitflag); rtrim(whole,' '); return (flag); } char* rtrim(char* string, char junk) { char* original = string + strlen(string); while(*--original == junk); *(original + 1) = '\0'; return string; } void insert(char *st, const char *s2, const int location) { char temp[MAXIMUMUPUTSTRINGLENGTH]; int counter, tempstringlength; strncpy(temp,st,location); temp[location]='\0'; strcat(temp,s2); tempstringlength=strlen(temp); for (counter=location; st[counter] != '\0' && counter < MAXIMUMUPUTSTRINGLENGTH; counter++, tempstringlength++) temp[tempstringlength]=st[counter]; temp[tempstringlength]='\0'; strcpy(st,temp); return; }
DOS COLOR FUNCTION
A simple function to assign colors to text based on the MS-DOS color numbers. This function puts all text on a cyan background; you can change it to any background color that you require.
void start_dos_colors(void) { init_pair(1,0,6); // black init_pair(2,3,6); // blue init_pair(3,2,6); // green init_pair(4,1,6); // red init_pair(5,5,6); // magenta init_pair(6,3,6); // brown init_pair(7,7,6); // white return; } void doscolor(const int color) { switch (color) { case 0: attrset(COLOR_PAIR(1)); //black break; case 1: attrset(COLOR_PAIR(2)); // blue break; case 2: attrset(COLOR_PAIR(3)); //green break; case 3: // cyan -- not used on this cyan background break; case 4: attrset(COLOR_PAIR(4)); //red break; case 5: attrset(COLOR_PAIR(5)); //magenta break; case 6: attrset(COLOR_PAIR(6)); //brown break; case 7: attrset(COLOR_PAIR(7)); //white break; case 8: attrset(COLOR_PAIR(1)|A_BOLD); //gray break; case 9: attrset(COLOR_PAIR(2)|A_BOLD); //light blue break; case 10: attrset(COLOR_PAIR(3)|A_BOLD); //light green break; case 11: // light cyan -- not used on this cyan background break; case 12: attrset(COLOR_PAIR(4)|A_BOLD); // light red break; case 13: attrset(COLOR_PAIR(5)|A_BOLD); // light magenta break; case 14: attrset(COLOR_PAIR(6)|A_BOLD); // yellow break; case 15: attrset(COLOR_PAIR(7)|A_BOLD); // high intensity white break; } return; }
NCURSES BOX DRAWING FUNCTION
A simple function to draw a box.
void drawbox(const int y, const int x, const int width, const int height) //width and height must be at least 2 to allow for top and bottom line { int counter; mvaddch(y,x,ACS_ULCORNER); for (counter=2; counter < width; counter++) addch(ACS_HLINE); addch(ACS_URCORNER); for (counter=1; counter < height-1; counter++) { mvaddch(y+counter,x,ACS_VLINE); mvaddch(y+counter,x+width-1,ACS_VLINE); } mvaddch(y+height-1,x,ACS_LLCORNER); for (counter=2; counter < width; counter++) addch(ACS_HLINE); addch(ACS_LRCORNER); return; }
GEANY – A Lightweight IDE & Programmer's Editor
If you are looking for an excellent programmer's editor, I use and highly recommend Geany (homepage). Geany is a lightweight IDE that should run on most Linux and Unix systems, and apparently it even runs on Microsoft Windows. It requires only the GTK2 runtime libraries.
You can download precompiled Geany rpms for Centos 5, both x86_64 and i386 versions, from my “Assorted RPMS for Centos 5” page. You can download Geany for everything else here.
I am not associated in any way with the developers of Geany, other than the fact that I use and really like their editor.
Other articles written by Frank Cox can be found here.
Frank Cox owns and operates the Melville Theatre in Melville, Saskatchewan, Canada, and has been playing with computers for over 30 years.
This work is licensed under a Creative
Commons Attribution-Share Alike 2.5 Canada License.