/*********************************************************
**********************************************************

April 5, 2002
Tyler Wong
neoncap@sharkfeeder.com
http://www.sharkfeeder.com



textToVCard is an application written for Macintosh PPC.  It 
converts textfiles to VCard format, which can be displayed 
by the iPod.  Unlike other scripts I've seen, this program 
will create files that have no practical size limitation.  
The key is that it uses the e-mail fields, on which iPod 
imposes no space limitations.

Disclaimer: I'm not a professional, or even a good, 
programmer, so this program might be bugfilled, inefficient, 
user-unfriendly, etc.  I will say that it has worked for me 
with no problems.  Any and all comments are welcome, 
suggestions, bug reports, condescension, offers to help, etc.

For more on this program, visit
http://www.sharkfeeder.com/ipod


**********************************************************
**********************************************************/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>


int DEFAULT_CHAR_WIDTH;
int INDENT;
int MAXWIDTH;
int SMARTINDENT;


 typedef enum {
  LEADING_SPACE, END, LEADING_PUNC, MEAT, TRAILING_PUNC
 } state_type;


//**************************************
// prototypes
//**************************************

int main (void) ;
void processFile (FILE* infile, FILE* outfile) ;
void tokToListname(char* tok, char* listname) ;
void initOutfile (FILE* outfile) ;
void printStrToFile(FILE* outfile, char* s) ;
int getWordWidth(char* word) ;
int getCharWidth(char c) ;
void processLineAsText (char* instr, FILE* outfile) ;
void getNextFragment(char* m, char* frag) ;
int getIndentValue (char* p) ;


//**************************************
//**************************************
//
// main
//
//**************************************
//**************************************


int main (void) {
 FILE* infile;
 FILE* flf;
 FILE* outfile;
 char instr[80];
 char srcname[80];
 char outname[80];
 if( (flf=fopen("filelist.txt", "r")) == NULL) {
  printf("filelist.txt can't be opened\n");
 }
 else {
  while( fgets(instr, 80, flf)) {
   if(strlen(instr)>0) {
	  strcpy(srcname, strtok(instr, " \n"));
	  strcpy(outname, strtok(NULL, " \n"));
	  if( (infile=fopen(srcname, "r")) == NULL) {
     printf("%s can't be opened\n", srcname);
	  }
	  else if( (outfile=fopen(outname, "w")) == NULL) {
     printf("%s can't be opened\n", outname);
	  }
	  else {
	   processFile(infile, outfile);
printf("converted \"%s\" to \"%s\"\n", srcname, outname); 
	  }
	 }
	}
 }
}


//**************************************
// processFile
//**************************************


void processFile (FILE* infile, FILE* outfile) {
 char instr[10000] = {0};
 char tempin[100] = {0};
 char outstr[200] = {0};
 char listname[100] = {0};
 char fragment[1000] = {0};
 char temp[10] = {0};
 char* p;
 char* tok;
 int i;

//
// initialize tasklist name.  preceded by * so it shows up
// first in the contacts list
//
 strcpy(listname, "TextToVCardFile\0");
 DEFAULT_CHAR_WIDTH = 8;
 INDENT = 1;
 MAXWIDTH = 144;
 SMARTINDENT = 0;
 
//
// check for file i/o errors
//

  initOutfile(outfile);

//
// while not at the end of source file
//
  while(fgets(instr, sizeof(instr), infile)) {

//
// if line is set off by the title command, set the listname
//

   p = strchr(instr, '\n');
   if(p!=NULL) { *p = '\0'; }
   strncpy(tempin, instr, sizeof(tempin));
   tempin[99] = '\0';
   tok = strtok(tempin, " \n");
   if(tok != NULL && strcmp(tok,".title")==0) {
    tok = strtok(NULL, "\0\n");
    if(strlen(tok)>0) {
     tokToListname(tok, listname);
    }
   }
//
// else if line is set off by smartindent command, set the smartindent value
//
   else if (tok != NULL && strcmp(tok,".smartindent")==0) {
    tok = strtok(NULL, " \n");
    if(strlen(tok)>0) {
     i = atol(tok);
     if(i!=0) { i=1; }
     SMARTINDENT = i;
    }
   }
//
// else if line is set off by indent command, set the indent value
//
   else if (tok != NULL && strcmp(tok,".indent")==0) {
    tok = strtok(NULL, " \n");
    if(strlen(tok)>0) {
     i = atol(tok);
     if(i>40 || i<0) { i=1; }
     INDENT = i;
    }
   }
//
// else if line is set off by documentwidth command, set the indent value
//
   else if (tok != NULL && strcmp(tok,".documentwidth")==0) {
    tok = strtok(NULL, " \n");
    if(strlen(tok)>0) {
     i = atol(tok);
     if(i<20) { i=144; }
     MAXWIDTH = i;
    }
   }
//
// else if line is set off by the default char width command
//   
   else if (tok != NULL && strcmp(tok,".defaultcharwidth")==0) {
    tok = strtok(NULL, " \n");
    if(strlen(tok)>0) {
     i = atol(tok);
     if(i>20 || i<1) { i=6; }
     DEFAULT_CHAR_WIDTH = i;
    }   
   }
//
// else, process the line as text
//
   else {
    processLineAsText(instr, outfile);
   }
  } 

//  
// print file closer 
//
  fprintf(outfile, "fn:%s\n", listname);
  fprintf(outfile, "n:%s\n", listname);
  fprintf(outfile, "end:vcard\n"); 
}



//**************************************
// processLineAsText
//**************************************


void processLineAsText (char* instr, FILE* outfile) {
 char* ptr;
 char fragment[5000] = {0};
 char outstr[150] = {0};
 char* n;
 char poo[10];
 int SmIndVal;
 bool done;
 int width;
 int i;
 
 
 SmIndVal=getIndentValue(instr);
 ptr=instr;
 done=0;
 width=0;
 outstr[0] = '\0';
 getNextFragment(ptr, fragment);
 ptr+=strlen(fragment);
// printf("PLAT1: indval: %d  ptr: [%s]  frag: [%s]  width: [%d]\n", SmIndVal, ptr, fragment, width);
 while(!done) {
  width=getWordWidth(outstr);
  if(strlen(fragment)==0) {
// printf("PLATcp2\n");
   done=1;
// printf("PLATcp6  **printing to file: %s\n", outstr);
   printStrToFile(outfile, outstr);
  }
  else if(width+getWordWidth(fragment)<=MAXWIDTH) {
// printf("PLATcp3\n");
   strcat(outstr, fragment);
   getNextFragment (ptr, fragment);
   ptr+=strlen(fragment);
  }
  else {
// printf("PLATcp4 os: %s  frag: %s\n", outstr, fragment);
   n=fragment;
   if( ( getWordWidth(fragment)+ (getCharWidth(' ')*SmIndVal) ) > MAXWIDTH) {
    while( (width+(i=getCharWidth(*n)))<=MAXWIDTH) {
     sprintf(outstr, "%s%c\0", outstr, *n);
     width+=i; n++;
    }
   }
// printf("PLATcp5  **printing to file: %s\n", outstr);
   printStrToFile(outfile, outstr);
   while(*n==' ') { n++; }
   strcpy(fragment, n);
   
   for(i=0;i<SmIndVal;i++) { outstr[i] = ' '; }
   outstr[i] = '\0';
  }

// width=getWordWidth(outstr);

// printf("PLAT2: indval: %d  ptr: [%s]  frag: [%s]  outstr: [%s] width: [%d]\n", SmIndVal, ptr, fragment, outstr, width);
// fgets(poo, 5, stdin);
 }
}



//**************************************
// getNextFragment
//**************************************

void getNextFragment(char* m, char* frag) {

 state_type state;
 char* s;
 int i;
 
 s=m;
 if(*s==' ') { state=LEADING_SPACE; }
 else if (*s=='\0') { state=END; }
 else if(ispunct(*s)) { state=LEADING_PUNC; }
 else { state=MEAT; }

 while(state!=END) {
  if(*s=='\0') {
   state=END;
  }
  else if(state==MEAT) {
   if(*s == ' ') {   state=END;  }
   else if(ispunct(*s)) {   state=TRAILING_PUNC;  }
   else {    s++;   }
  }
  else if(state==LEADING_SPACE) {
   if(*s == ' ') {   s++;  }
   else if(ispunct(*s)) {   state=LEADING_PUNC;  }
   else {    state=MEAT;   }
  }
  else if(state==LEADING_PUNC) {
   if(*s == ' ') {   state=END;  }
   else if(ispunct(*s)) {   s++;  }
   else {    state=MEAT;   }
  }
  else if(state==TRAILING_PUNC) {
   if(*s == ' ') {   state=END;  }
   else if(ispunct(*s)) {   s++;  }
   else {    state=END;   }
  }
 }
 frag[0] = '\0';
 i=0;
 while(m<s && i++<5000) {
  sprintf(frag, "%s%c\0", frag, *m++);
 }  
}  



//**************************************
// getIndentValue
//**************************************

int getIndentValue (char* p) {
 int retval;
 retval = INDENT;
 if(SMARTINDENT && p!=NULL) {
  while(*p++==' ') retval++;
 }
 return(retval);
}


//**************************************
// tokToListname
//**************************************

void tokToListname(char* tok, char* listname) {
 int i = 0;
 int len;
 len = strlen(tok);
 listname[0]='\0';
 for(i=0;i<len && i<100;i++) { 
  switch(tok[i]) {
   case ';':
   case ',':
    strcat(listname, "\\");
    break;
   default:
    break;
  }
  sprintf(listname, "%s%c", listname, tok[i]);
 }
}
 
 
//**************************************
// initOutfile
//**************************************

void initOutfile (FILE* outfile) {
 fprintf(outfile, "begin:vcard\nversion:3.0\n");
}


//**************************************
// printStrToFile
//**************************************

void printStrToFile(FILE* outfile, char* s) {
 int i = 0;
 static int ctr = 0;
 int len;
 len = strlen(s);
 fprintf(outfile, "email:", ctr++);
 for(i=0;i<len;i++) { 
  switch(s[i]) {
   case ',':
   case ';':
   fprintf(outfile, "\\");
    break;
   default:
    break;
  }
  if(s[i]!='\n' && s[i]!='\0') {
   fprintf(outfile, "%c", s[i]);
  }
 }
 fprintf(outfile, "\n");
 s[0] = '\0';
}


//**************************************
//**************************************
//*
//* WIDTH FUNCTIONS
//*
//**************************************
//**************************************


//**************************************
// getWordWidth
//**************************************

int getWordWidth(char* word) {
 int i, len;
 int width=0;
 len = strlen(word);
 for(i=0;i<len;i++) {
  width += getCharWidth(word[i]);
 }
 return(width);
}

//**************************************
// getCharWidth
//**************************************

int getCharWidth(char c) {
 int ret;
 switch(c) {
  case '`': ret = 3; break;
  case '1': ret = 7; break;
  case '2': ret = 7; break;
  case '3': ret = 7; break;
  case '4': ret = 7; break;
  case '5': ret = 7; break;
  case '6': ret = 7; break;
  case '7': ret = 7; break;
  case '8': ret = 7; break;
  case '9': ret = 7; break;
  case '0': ret = 7; break;
  case '-': ret = 4; break;
  case '=': ret = 7; break;
  case 'q': ret = 6; break;
  case 'w': ret = 10; break;
  case 'e': ret = 6; break;
  case 'r': ret = 4; break;
  case 't': ret = 5; break;
  case 'y': ret = 6; break;
  case 'u': ret = 6; break;
  case 'i': ret = 2; break;
  case 'o': ret = 6; break;
  case 'p': ret = 6; break;
  case '[': ret = 3; break;
  case ']': ret = 3; break;
  case '\\': ret = 5; break;
  case 'a': ret = 6; break;
  case 's': ret = 5; break;
  case 'd': ret = 6; break;
  case 'f': ret = 4; break;
  case 'g': ret = 6; break;
  case 'h': ret = 6; break;
  case 'j': ret = 3; break;
  case 'k': ret = 5; break;
  case 'l': ret = 2; break;
  case ';': ret = 3; break;
  case '\'': ret = 2; break;
  case 'z': ret = 5; break;
  case 'x': ret = 6; break;
  case 'c': ret = 5; break;
  case 'v': ret = 6; break;
  case 'b': ret = 6; break;
  case 'n': ret = 6; break;
  case 'm': ret = 10; break;
  case ',': ret = 3; break;
  case '.': ret = 3; break;
  case '/': ret = 5; break;
  case '~': ret = 7; break;
  case '!': ret = 3; break;
  case '@': ret = 9; break;
  case '#': ret = 9; break;
  case '$': ret = 6; break;
  case '%': ret = 9; break;
  case '^': ret = 6; break;
  case '&': ret = 8; break;
  case '*': ret = 6; break;
  case '(': ret = 4; break;
  case ')': ret = 4; break;
  case '_': ret = 7; break;
  case '+': ret = 6; break;
  case 'Q': ret = 8; break;
  case 'W': ret = 10; break;
  case 'E': ret = 6; break;
  case 'R': ret = 6; break;
  case 'T': ret = 7; break;
  case 'Y': ret = 8; break;
  case 'U': ret = 7; break;
  case 'I': ret = 3; break;
  case 'O': ret = 8; break;
  case 'P': ret = 6; break;
  case '{': ret = 4; break;
  case '}': ret = 4; break;
  case '|': ret = 4; break;
  case 'A': ret = 8; break;
  case 'S': ret = 6; break;
  case 'D': ret = 7; break;
  case 'F': ret = 6; break;
  case 'G': ret = 7; break;
  case 'H': ret = 7; break;
  case 'J': ret = 5; break;
  case 'K': ret = 6; break;
  case 'L': ret = 6; break;
  case ':': ret = 3; break;
  case '"': ret = 4; break;
  case 'Z': ret = 7; break;
  case 'X': ret = 7; break;
  case 'C': ret = 7; break;
  case 'V': ret = 8; break;
  case 'B': ret = 6; break;
  case 'N': ret = 7; break;
  case 'M': ret = 10; break;
  case '<': ret = 6; break;
  case '>': ret = 6; break;
  case '?': ret = 6; break;
  case ' ': ret = 3; break;
  default: ret = DEFAULT_CHAR_WIDTH;
 }
 return(ret);
}


