Пример #1
0
static size_t curlHeaderCallback(void *ptr, size_t size, size_t nmemb, void *opaque)
{
    Hashtable<String,String>* pHeaders = (Hashtable<String,String>*)opaque;
    size_t nBytes = size*nmemb;
    String strHeader((const char *)ptr, nBytes);
    RAWTRACE1("Received header: %s", strHeader.c_str());
    
    int nSep = strHeader.find(':');
    if (nSep > 0 )
    {		
        String strName = String_trim(strHeader.substr(0, nSep));
        String lName;
        std::transform(strName.begin(), strName.end(), std::back_inserter(lName), &::tolower);
        
        String strValue = String_trim(strHeader.substr(nSep+1, strHeader.length() - (nSep+3) ));
        
        if ( pHeaders->containsKey(lName) )
        {
            strValue += ";" + pHeaders->get( lName );
            pHeaders->put( lName, strValue );
        }
        else
            pHeaders->put(lName, strValue);
    }
    
    return nBytes;
}
Пример #2
0
static Htop_Reaction sortBy(State* st) {
   Htop_Reaction reaction = HTOP_OK;
   Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort   ", "Cancel "));
   Panel_setHeader(sortPanel, "Sort by");
   ProcessField* fields = st->settings->fields;
   for (int i = 0; fields[i]; i++) {
      char* name = String_trim(Process_fields[fields[i]].name);
      Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
      if (fields[i] == st->settings->sortKey)
         Panel_setSelected(sortPanel, i);
      free(name);
   }
   ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15);
   if (field) {
      reaction |= Action_setSortKey(st->settings, field->key);
   }
   Object_delete(sortPanel);
   return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
Пример #3
0
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header) {
   Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
   Panel_setHeader(sortPanel, "Sort by");
   const char* fuFunctions[] = {"Sort  ", "Cancel ", NULL};
   ProcessField* fields = pl->fields;
   for (int i = 0; fields[i]; i++) {
      char* name = String_trim(Process_fieldNames[fields[i]]);
      Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
      if (fields[i] == pl->sortKey)
         Panel_setSelected(sortPanel, i);
      free(name);
   }
   ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
   if (field) {
      settings->changed = true;
      setSortKey(pl, defaultBar, field->key, panel, settings);
   } else {
      ProcessList_printHeader(pl, Panel_getHeader(panel));
   }
   Object_delete(sortPanel);
}
Пример #4
0
bool CHttpServer::send_file(String const &path, HeaderList const &hdrs)
{
    String fullPath = CFilePath::normalizePath(path);

    if (String_startsWith(fullPath,"/app/db/db-files") )
        fullPath = CFilePath::join( rho_native_rhodbpath(), path.substr(4) );
    else if (fullPath.find(m_root) != 0 && fullPath.find(m_strRhoRoot) != 0 && fullPath.find(m_strRuntimeRoot) != 0 && fullPath.find(m_userroot) != 0 && fullPath.find(m_strRhoUserRoot) != 0)
        fullPath = CFilePath::join( m_root, path );
	
    struct stat st;
    bool bCheckExist = true;
#ifdef RHODES_EMULATOR
    String strPlatform = RHOSIMCONF().getString("platform");
    if ( strPlatform.length() > 0 )
    {
        String fullPath1 = fullPath;
        int nDot = fullPath1.rfind('.');
        if ( nDot >= 0 )
            fullPath1.insert(nDot, String(".") + strPlatform);
        else
            fullPath1 += String(".") + strPlatform;

        if (stat(fullPath1.c_str(), &st) == 0 && S_ISREG(st.st_mode))
        {
            fullPath = fullPath1;
            bCheckExist = false;
        }
    }

#endif

    bool doesNotExists = bCheckExist && (stat(fullPath.c_str(), &st) != 0 || !S_ISREG(st.st_mode));
    if ( doesNotExists ) {
        // looking for files at 'rho/apps' at runtime folder
        fullPath = CFilePath::join( m_strRuntimeRoot, path );
    }

    if (verbose) RAWTRACE1("Sending file %s...", fullPath.c_str());

    if ( doesNotExists ) {
        if ( stat(fullPath.c_str(), &st) != 0 || !S_ISREG(st.st_mode) ) {
            doesNotExists = true;
        }else
            doesNotExists = false;
    }

#ifdef RHODES_EMULATOR
    if ( doesNotExists )
    {
        CTokenizer oTokenizer( RHOSIMCONF().getString("ext_path"), ";" );
	    while (oTokenizer.hasMoreTokens()) 
        {
		    String tok = oTokenizer.nextToken();
		    tok = String_trim(tok);
            
            String fullPath1 = CFilePath::join( tok, path );
            if (stat(fullPath1.c_str(), &st) == 0 && S_ISREG(st.st_mode))
            {
                fullPath = fullPath1;
                doesNotExists = false;
                break;
            }

        }
    }
#endif

    if ( doesNotExists ) {
        if (verbose) RAWLOG_ERROR1("The file %s was not found", path.c_str());
        String error = "<!DOCTYPE html><html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + path + " was not found.</font></html>";
        send_response(create_response("404 Not Found",error));
        return false;
    }

    PROF_START("LOW_FILE");
    FILE *fp = fopen(fullPath.c_str(), "rb");
    PROF_STOP("LOW_FILE");
    if (!fp) {
        if (verbose) RAWLOG_ERROR1("The file %s could not be opened", path.c_str());
        String error = "<!DOCTYPE html><html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + path + " could not be opened.</font></html";
        send_response(create_response("404 Not Found",error));
        return false;
    }
    
    HeaderList headers;
    
    // Detect mime type
    headers.push_back(Header("Content-Type", get_mime_type(path)));
    if ( String_startsWith(path, "/public") )
    {
        headers.push_back(Header("Expires", "Thu, 15 Apr 2020 20:00:00 GMT") );
        headers.push_back(Header("Cache-Control", "max-age=2592000") );
    }

    // Content length
    char* buf = new char[FILE_BUF_SIZE];
    
    String start_line;
    
    size_t file_size = st.st_size;
    size_t range_begin = 0, range_end = file_size - 1;
    size_t content_size = file_size;
    if (parse_range(hdrs, &range_begin, &range_end))
    {
        if (range_end >= file_size)
            range_end = file_size - 1;
        if (range_begin >= range_end)
            range_begin = range_end - 1;
        content_size = range_end - range_begin + 1;
        
        if (fseek(fp, range_begin, SEEK_SET) == -1) {
            RAWLOG_ERROR1("Can not seek to specified range start: %lu", (unsigned long)range_begin);
			snprintf(buf, FILE_BUF_SIZE, "bytes */%lu", (unsigned long)file_size);
			headers.push_back(Header("Content-Range", buf));
			send_response(create_response("416 Request Range Not Satisfiable",headers));
            fclose(fp);
            delete[] buf;
            return false;
        }
		
		snprintf(buf, FILE_BUF_SIZE, "bytes %lu-%lu/%lu", (unsigned long)range_begin,
                 (unsigned long)range_end, (unsigned long)file_size);
        headers.push_back(Header("Content-Range", buf));
        
        start_line = "206 Partial Content";
    }
    else {
        start_line = "200 OK";
    }

    
    snprintf(buf, FILE_BUF_SIZE, "%lu", (unsigned long)content_size);
    headers.push_back(Header("Content-Length", buf));
    
    // Send headers
    if (!send_response(create_response(start_line, headers))) {
        if (verbose) RAWLOG_ERROR1("Can not send headers while sending file %s", path.c_str());
        fclose(fp);
        delete[] buf;
        return false;
    }
    
    // Send body
    for (size_t start = range_begin; start < range_end + 1;) {
        size_t need_to_read = range_end - start + 1;
        if (need_to_read == 0)
            break;
        
        if (need_to_read > FILE_BUF_SIZE)
            need_to_read = FILE_BUF_SIZE;

PROF_START("LOW_FILE");
        size_t n = fread(buf, 1, need_to_read, fp);//fread(buf, 1, need_to_read, fp);
PROF_STOP("LOW_FILE");
        if (n < need_to_read) {
			if (ferror(fp) ) {
				if (verbose) RAWLOG_ERROR2("Can not read part of file (at position %lu): %s", (unsigned long)start, strerror(errno));
			} else if ( feof(fp) ) {
				if (verbose) RAWLOG_ERROR1("End of file reached, but we expect data (%lu bytes)", (unsigned long)need_to_read);
			}
            fclose(fp);
            delete[] buf;
            return false;
        }
        
        start += n;
        
        if (!send_response_body(String(buf, n))) {
            if (verbose) RAWLOG_ERROR1("Can not send part of data while sending file %s", path.c_str());
            fclose(fp);
            delete[] buf;
            return false;
        }
    }

PROF_START("LOW_FILE");
    fclose(fp);
PROF_STOP("LOW_FILE");
    delete[] buf;
    if (verbose) RAWTRACE1("File %s was sent successfully", path.c_str());
    return false;
}
Пример #5
0
bool CHttpServer::dispatch(String const &uri, Route &route)
{
    if (isknowntype(uri))
        return false;
    
    // Trying to parse route
    if (!parse_route(uri, route))
        return false;
    
    // Convert CamelCase to underscore_case
    // There has to be a better way?
    char tmp[3];
    const char *tmpstr = route.model.c_str();
    String controllerName = "";
    for(int i = 0; tmpstr[i] != '\0'; i++) {
        if(tmpstr[i] >= 'A' && tmpstr[i] <= 'Z') {
            if(i == 0) {
                tmp[0] = tmpstr[i] + 0x20;
                tmp[1] = '\0';
            } else {
                tmp[0] = '_';
                tmp[1] = tmpstr[i] + 0x20;
                tmp[2] = '\0';
            }
        } else {
            tmp[0] = tmpstr[i];
            tmp[1] = '\0';
        }
        controllerName += tmp;
    }
        
    //check if there is controller.rb to run
	struct stat st;

    String newfilename = CFilePath::join(m_root, route.application) + "/" + route.model + "/" + controllerName + "_controller" RHO_RB_EXT;
    String filename = CFilePath::join(m_root, route.application) + "/" + route.model + "/controller" RHO_RB_EXT;

    //look for controller.rb or model_name_controller.rb
    if ((stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) && (stat(newfilename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)))
    {
#ifdef RHODES_EMULATOR
        CTokenizer oTokenizer( RHOSIMCONF().getString("ext_path"), ";" );
	    while (oTokenizer.hasMoreTokens()) 
        {
		    String tok = oTokenizer.nextToken();
		    tok = String_trim(tok);

            newfilename = CFilePath::join(tok, route.application) + "/" + route.model + "/" + controllerName + "_controller" RHO_RB_EXT;
            filename = CFilePath::join(tok, route.application) + "/" + route.model + "/controller" RHO_RB_EXT;

            //look for controller.rb or model_name_controller.rb
            if ((stat(filename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) && (stat(newfilename.c_str(), &st) != 0 || !S_ISREG(st.st_mode)))
                continue;

            return true;
        }
#endif

        return false;
    }

    return true;
}
Пример #6
0
/*static*/ void URI::parseCookie(const char* szCookie, String& strRes) 
{
    common::CTokenizer stringtokenizer(szCookie, ";");
	while (stringtokenizer.hasMoreTokens()) 
    {
		String tok = stringtokenizer.nextToken();
		tok = String_trim(tok);
		if (tok.length() == 0) {
			continue;
		}
		
		//expires=Thu, 01 Jan 1970 00:00:00 GMT, auth_token=
		int nExp = tok.find("expires=");
		if ( nExp >= 0 )
		{
			int nEnd = tok.find(',', nExp);
			if ( nEnd >= 0 )
			{
				int nEnd1 = tok.find(',', nEnd+1);
				if ( nEnd1 >= 0 )
					nEnd = nEnd1;
				else
					nEnd = tok.length()-1;
			}
			
			tok = tok.substr(0,nExp) + tok.substr(nEnd+1);
			tok = String_trim(tok);
		}
		
		int nEq = tok.find('=');
		if ( nEq < 0 )
			continue;
		
		strRes += tok + ";";  
/*
		int i = 0;
		if ( (i=tok.find("auth_token=")) >= 0 )
		{
			String val = trim(tok.substr(i+11));
			if ( val.length() > 0 )
			{
				cookie.strAuth = "auth_token=" + val;
				bAuth = true;
			}
		}else if ( (i=tok.find("path=")) >= 0 )
		{
			String val = trim(tok.substr(i+6));
			if ( val.length() > 0 )
			{
				if (bAuth)
					cookie.strAuth += ";path=" + val;
				else if (bSession)
					cookie.strSession += ";path=" + val;
			}
		}else if ( (i=tok.find("rhosync_session=")) >= 0 )
		{
			String val = trim(tok.substr(i+16));
			if ( val.length() > 0 )
			{
				cookie.strSession = "rhosync_session=" + val;
				bSession = true;
			}
		} */
	}
}
Пример #7
0
int main(int argc, char** argv) {

   int delay = -1;
   bool userOnly = false;
   uid_t userId = 0;
   int usecolors = 1;
   char *argCopy;
   char *pid;
   Hashtable *pidWhiteList = NULL;

   int opt, opti=0;
   static struct option long_opts[] =
   {
      {"help",     no_argument,         0, 'h'},
      {"version",  no_argument,         0, 'v'},
      {"delay",    required_argument,   0, 'd'},
      {"sort-key", required_argument,   0, 's'},
      {"user",     required_argument,   0, 'u'},
      {"no-color", no_argument,         0, 'C'},
      {"no-colour",no_argument,         0, 'C'},
      {"pid",      required_argument,   0, 'p'},
      {0,0,0,0}
   };
   int sortKey = 0;

   char *lc_ctype = getenv("LC_CTYPE");
   if(lc_ctype != NULL)
      setlocale(LC_CTYPE, lc_ctype);
   else if ((lc_ctype = getenv("LC_ALL")))
      setlocale(LC_CTYPE, lc_ctype);
   else
      setlocale(LC_CTYPE, "");

   /* Parse arguments */
   while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:", long_opts, &opti))) {
      if (opt == EOF) break;
      switch (opt) {
         case 'h':
            printHelpFlag();
            break;
         case 'v':
            printVersionFlag();
            break;
         case 's':
            if (strcmp(optarg, "help") == 0) {
               for (int j = 1; j < LAST_PROCESSFIELD; j++)
                  printf ("%s\n", Process_fieldNames[j]);
               exit(0);
            }

            sortKey = ColumnsPanel_fieldNameToIndex(optarg);
            if (sortKey == -1) {
               fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
               exit(1);
            }
            break;
         case 'd':
            if (sscanf(optarg, "%d", &delay) == 1) {
               if (delay < 1) delay = 1;
               if (delay > 100) delay = 100;
            } else {
               fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
               exit(1);
            }
            break;
         case 'u':
            if (!setUserOnly(optarg, &userOnly, &userId)) {
               fprintf(stderr, "Error: invalid user \"%s\".\n", optarg);
               exit(1);
            }
            break;
         case 'C':
            usecolors=0;
            break;
        case 'p':
            argCopy = strdup(optarg);
            pid = strtok(argCopy, ",");

            if( !pidWhiteList ) {
               pidWhiteList = Hashtable_new(8, false);
            }

            while( pid ) {
                unsigned int num_pid = atoi(pid);
                Hashtable_put(pidWhiteList, num_pid, (void *) 1);
                pid = strtok(NULL, ",");
            }
            free(argCopy);

            break;
         default:
            exit(1);
      }
   }


   if (access(PROCDIR, R_OK) != 0) {
      fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
      exit(1);
   }

   int quit = 0;
   int refreshTimeout = 0;
   int resetRefreshTimeout = 5;
   bool doRefresh = true;
   bool doRecalculate = false;
   Settings* settings;
   
   ProcessList* pl = NULL;
   UsersTable* ut = UsersTable_new();

#ifdef HAVE_LIBNCURSESW
   char *locale = setlocale(LC_ALL, NULL);
   if (locale == NULL || locale[0] == '\0')
      locale = setlocale(LC_CTYPE, NULL);
   if (locale != NULL &&
       (strstr(locale, "UTF-8") ||
        strstr(locale, "utf-8") ||
        strstr(locale, "UTF8")  ||
        strstr(locale, "utf8")))
      CRT_utf8 = true;
   else
      CRT_utf8 = false;
#endif

   pl = ProcessList_new(ut, pidWhiteList);
   Process_getMaxPid();
   
   Header* header = Header_new(pl);
   settings = Settings_new(pl, header, pl->cpuCount);
   int headerHeight = Header_calculateHeight(header);

   // FIXME: move delay code to settings
   if (delay != -1)
      settings->delay = delay;
   if (!usecolors) 
      settings->colorScheme = COLORSCHEME_MONOCHROME;

   CRT_init(settings->delay, settings->colorScheme);

   Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class);
   ProcessList_setPanel(pl, panel);
   
   if (sortKey > 0) {
      pl->sortKey = sortKey;
      pl->treeView = false;
      pl->direction = 1;
   }
   ProcessList_printHeader(pl, Panel_getHeader(panel));
   
   const char* defaultFunctions[] = {"Help  ", "Setup ", "Search", "Filter", "Tree  ",
       "SortBy", "Nice -", "Nice +", "Kill  ", "Quit  ", NULL};
   FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);

   IncSet* inc = IncSet_new(defaultBar);

   ProcessList_scan(pl);
   usleep(75000);
   
   FunctionBar_draw(defaultBar, NULL);
   
   int acc = 0;
   bool follow = false;
 
   struct timeval tv;
   double newTime = 0.0;
   double oldTime = 0.0;
   bool recalculate;

   int ch = ERR;
   int closeTimeout = 0;

   bool idle = false;
   
   while (!quit) {
      gettimeofday(&tv, NULL);
      newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
      recalculate = (newTime - oldTime > settings->delay);
      Process* p = (Process*)Panel_getSelected(panel);
      int following = (follow && p) ? p->pid : -1;
      if (recalculate) {
         Header_draw(header);
         oldTime = newTime;
      }
      if (doRefresh) {
         if (recalculate || doRecalculate) {
            ProcessList_scan(pl);
            doRecalculate = false;
         }
         if (refreshTimeout == 0 || pl->treeView) {
            ProcessList_sort(pl);
            refreshTimeout = 1;
         }
         ProcessList_rebuildPanel(pl, true, following, userOnly, userId, IncSet_filter(inc));
         idle = false;
      }
      doRefresh = true;

      if (!idle)
         Panel_draw(panel, true);
      
      int prev = ch;
      if (inc->active)
         move(LINES-1, CRT_cursorX);
      ch = getch();

      if (ch == ERR) {
         if (!inc->active)
            refreshTimeout--;
         if (prev == ch && !recalculate) {
            closeTimeout++;
            if (closeTimeout == 100) {
               break;
            }
         } else
            closeTimeout = 0;
         idle = true;
         continue;
      }
      idle = false;

      if (ch == KEY_MOUSE) {
         MEVENT mevent;
         int ok = getmouse(&mevent);
         if (ok == OK) {
            if (mevent.bstate & BUTTON1_CLICKED) {
               if (mevent.y == panel->y) {
                  int x = panel->scrollH + mevent.x + 1;
                  ProcessField field = ProcessList_keyAt(pl, x);
                  if (field == pl->sortKey) {
                     ProcessList_invertSortOrder(pl);
                     pl->treeView = false;
                  } else {
                     setSortKey(pl, field, panel, settings);
                  }
                  refreshTimeout = 0;
                  continue;
               } else if (mevent.y >= panel->y + 1 && mevent.y < LINES - 1) {
                  Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
                  doRefresh = false;
                  refreshTimeout = resetRefreshTimeout;
                  follow = true;
                  continue;
               } if (mevent.y == LINES - 1) {
                  ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
               }
            } else if (mevent.bstate & BUTTON4_CLICKED) {
               ch = KEY_UP;
            #if NCURSES_MOUSE_VERSION > 1
            } else if (mevent.bstate & BUTTON5_CLICKED) {
               ch = KEY_DOWN;
            #endif
            }
         }
      }

      if (inc->active) {
         doRefresh = IncSet_handleKey(inc, ch, panel, getMainPanelValue, NULL);
         continue;
      }
      
      if (isdigit((char)ch)) {
         if (Panel_size(panel) == 0) continue;
         pid_t pid = ch-48 + acc;
         for (int i = 0; i < ProcessList_size(pl) && ((Process*) Panel_getSelected(panel))->pid != pid; i++)
            Panel_setSelected(panel, i);
         acc = pid * 10;
         if (acc > 10000000)
            acc = 0;
         continue;
      } else {
         acc = 0;
      }

      switch (ch) {
      case KEY_RESIZE:
         Panel_resize(panel, COLS, LINES-headerHeight-1);
         IncSet_drawBar(inc);
         break;
      case 'M':
      {
         refreshTimeout = 0;
         setSortKey(pl, PERCENT_MEM, panel, settings);
         break;
      }
      case 'T':
      {
         refreshTimeout = 0;
         setSortKey(pl, TIME, panel, settings);
         break;
      }
      case 'U':
      {
         for (int i = 0; i < Panel_size(panel); i++) {
            Process* p = (Process*) Panel_get(panel, i);
            p->tag = false;
         }
         doRefresh = true;
         break;
      }
      case 'P':
      {
         refreshTimeout = 0;
         setSortKey(pl, PERCENT_CPU, panel, settings);
         break;
      }
      case KEY_F(1):
      case 'h':
      case '?':
      {
         showHelp(pl);
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case '\014': // Ctrl+L
      {
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case ' ':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         Process_toggleTag(p);
         Panel_onKey(panel, KEY_DOWN);
         break;
      }
      case 's':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         TraceScreen* ts = TraceScreen_new(p);
         TraceScreen_run(ts);
         TraceScreen_delete(ts);
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         CRT_enableDelay();
         break;
      }
      case 'l':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         OpenFilesScreen* ts = OpenFilesScreen_new(p);
         OpenFilesScreen_run(ts);
         OpenFilesScreen_delete(ts);
         clear();
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         CRT_enableDelay();
         break;
      }
      case 'S':
      case 'C':
      case KEY_F(2):
      {
         Setup_run(settings, header);
         // TODO: shouldn't need this, colors should be dynamic
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         headerHeight = Header_calculateHeight(header);
         Panel_move(panel, 0, headerHeight);
         Panel_resize(panel, COLS, LINES-headerHeight-1);
         FunctionBar_draw(defaultBar, NULL);
         refreshTimeout = 0;
         break;
      }
      case 'F':
      {
         follow = true;
         continue;
      }
      case 'u':
      {
         Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
         Panel_setHeader(usersPanel, "Show processes of:");
         UsersTable_foreach(ut, addUserToVector, usersPanel);
         Vector_insertionSort(usersPanel->items);
         ListItem* allUsers = ListItem_new("All users", -1);
         Panel_insert(usersPanel, 0, (Object*) allUsers);
         const char* fuFunctions[] = {"Show    ", "Cancel ", NULL};
         ListItem* picked = (ListItem*) pickFromVector(panel, usersPanel, 20, headerHeight, fuFunctions, defaultBar, header);
         if (picked) {
            if (picked == allUsers) {
               userOnly = false;
            } else {
               setUserOnly(ListItem_getRef(picked), &userOnly, &userId);
            }
         }
         Panel_delete((Object*)usersPanel);
         break;
      }
      case '+':
      case '=':
      case '-':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         p->showChildren = !p->showChildren;
         refreshTimeout = 0;
         doRecalculate = true;
         break;
      }
      case KEY_F(9):
      case 'k':
      {
         Panel* signalsPanel = (Panel*) SignalsPanel_new();
         const char* fuFunctions[] = {"Send  ", "Cancel ", NULL};
         ListItem* sgn = (ListItem*) pickFromVector(panel, signalsPanel, 15, headerHeight, fuFunctions, defaultBar, header);
         if (sgn) {
            if (sgn->key != 0) {
               Panel_setHeader(panel, "Sending...");
               Panel_draw(panel, true);
               refresh();
               foreachProcess(panel, (ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
               napms(500);
            }
         }
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         Panel_delete((Object*)signalsPanel);
         refreshTimeout = 0;
         break;
      }
#if (HAVE_LIBHWLOC || HAVE_NATIVE_AFFINITY)
      case 'a':
      {
         if (pl->cpuCount == 1)
            break;

         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         Affinity* affinity = Process_getAffinity(p);
         if (!affinity) break;
         Panel* affinityPanel = AffinityPanel_new(pl, affinity);
         Affinity_delete(affinity);

         const char* fuFunctions[] = {"Set    ", "Cancel ", NULL};
         void* set = pickFromVector(panel, affinityPanel, 15, headerHeight, fuFunctions, defaultBar, header);
         if (set) {
            Affinity* affinity = AffinityPanel_getAffinity(affinityPanel);
            bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
            if (!ok) beep();
            Affinity_delete(affinity);
         }
         Panel_delete((Object*)affinityPanel);
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         refreshTimeout = 0;
         break;
      }
#endif
      case KEY_F(10):
      case 'q':
         quit = 1;
         break;
      case '<':
      case ',':
      case KEY_F(18):
      case '>':
      case '.':
      case KEY_F(6):
      {
         Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
         Panel_setHeader(sortPanel, "Sort by");
         const char* fuFunctions[] = {"Sort  ", "Cancel ", NULL};
         ProcessField* fields = pl->fields;
         for (int i = 0; fields[i]; i++) {
            char* name = String_trim(Process_fieldNames[fields[i]]);
            Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
            if (fields[i] == pl->sortKey)
               Panel_setSelected(sortPanel, i);
            free(name);
         }
         ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
         if (field) {
            settings->changed = true;
            setSortKey(pl, field->key, panel, settings);
         } else {
            ProcessList_printHeader(pl, Panel_getHeader(panel));
         }
         Object_delete(sortPanel);
         refreshTimeout = 0;
         break;
      }
      case 'i':
      {
         Process* p = (Process*) Panel_getSelected(panel);
         if (!p) break;
         IOPriority ioprio = p->ioPriority;
         Panel* ioprioPanel = IOPriorityPanel_new(ioprio);
         const char* fuFunctions[] = {"Set    ", "Cancel ", NULL};
         void* set = pickFromVector(panel, ioprioPanel, 21, headerHeight, fuFunctions, defaultBar, header);
         if (set) {
            IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
            bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setIOPriority, (size_t) ioprio, NULL);
            if (!ok)
               beep();
         }
         Panel_delete((Object*)ioprioPanel);
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         refreshTimeout = 0;
         break;
      }
      case 'I':
      {
         refreshTimeout = 0;
         settings->changed = true;
         ProcessList_invertSortOrder(pl);
         break;
      }
      case KEY_F(8):
      case '[':
      {
         doRefresh = changePriority(panel, 1);
         break;
      }
      case KEY_F(7):
      case ']':
      {
         doRefresh = changePriority(panel, -1);
         break;
      }
      case KEY_F(3):
      case '/':
         IncSet_activate(inc, INC_SEARCH);
         break;
      case KEY_F(4):
      case '\\':
         IncSet_activate(inc, INC_FILTER);
         refreshTimeout = 0;
         doRefresh = true;
         continue;
      case 't':
      case KEY_F(5):
         refreshTimeout = 0;
         pl->treeView = !pl->treeView;
         if (pl->treeView) pl->direction = 1;
         ProcessList_printHeader(pl, Panel_getHeader(panel));
         ProcessList_expandTree(pl);
         settings->changed = true;
         if (following != -1) continue;
         break;
      case 'H':
         doRecalculate = true;
         refreshTimeout = 0;
         pl->hideUserlandThreads = !pl->hideUserlandThreads;
         pl->hideThreads = pl->hideUserlandThreads;
         settings->changed = true;
         break;
      case 'K':
         doRecalculate = true;
         refreshTimeout = 0;
         pl->hideKernelThreads = !pl->hideKernelThreads;
         settings->changed = true;
         break;
      default:
         doRefresh = false;
         refreshTimeout = resetRefreshTimeout;
         Panel_onKey(panel, ch);
         break;
      }
      follow = false;
   }
   attron(CRT_colors[RESET_COLOR]);
   mvhline(LINES-1, 0, ' ', COLS);
   attroff(CRT_colors[RESET_COLOR]);
   refresh();
   
   CRT_done();
   if (settings->changed)
      Settings_write(settings);
   Header_delete(header);
   ProcessList_delete(pl);
   IncSet_delete(inc);
   FunctionBar_delete((Object*)defaultBar);
   Panel_delete((Object*)panel);
   UsersTable_delete(ut);
   Settings_delete(settings);
   if(pidWhiteList) {
      Hashtable_delete(pidWhiteList);
   }
   return 0;
}